Custom Digital Signature Issues with AWS CloudHSM

Product: PDFTron Python

Product Version: 9.3.0

Please give a brief summary of your issue:
Having issues implementing Custom Digital Signatures with AWS HSM offering

Please describe your issue and provide steps to reproduce it:
I am attempting to follow the Custom Signing guide here and the signing in python guide here but I am unable to get a valid certified document after trying numerous different variations of the following code:

from PDFNetPython3 import *
from request_signature import request_signature
import os
license = os.getenv('PDFTRON_LICENSE')
PDFNet.Initialize(license)

in_docpath = 'test.pdf'
in_outpath = 'test output.pdf'
in_cert_path = 'aws_hsm/customerCA.crt'
in_sig_field_name = 'CertFieldName'
chain_cert_paths = ['aws_hsm/manufacturer_chain.crt', 'aws_hsm/aws_chain.crt']
in_PAdES_signing_mode = False

doc = PDFDoc(in_docpath)
page1 = doc.GetPage(1)

digsig_field = doc.CreateDigitalSignatureField(in_sig_field_name)
widgetAnnot = SignatureWidget.Create(doc, Rect(143, 287, 219, 306), digsig_field)
page1.AnnotPushBack(widgetAnnot)

digsig_field.CreateSigDictForCustomCertification("Adobe.PPKLite",\
	DigitalSignatureField.e_ETSI_CAdES_detached if in_PAdES_signing_mode else DigitalSignatureField.e_adbe_pkcs7_detached,\
	7500)

current_date = Date()
current_date.SetCurrentTime()
digsig_field.SetSigDictTimeOfSigning(current_date)

doc.Save(in_outpath, SDFDoc.e_incremental)

pdf_digest = digsig_field.CalculateDigest(DigestAlgorithm.e_SHA256)

signer_cert = X509Certificate(in_cert_path)

signedAttrs = DigitalSignatureField.GenerateCMSSignedAttributes(pdf_digest)
signedAttrs_digest = DigestAlgorithm.CalculateDigest(DigestAlgorithm.e_SHA256, signedAttrs)

signature_value = request_signature(list(signedAttrs_digest))

chain_certs = []
chain_certs.append(X509Certificate(chain_cert_paths[0])) #manufacturer_chain.crt
chain_certs.append(X509Certificate(chain_cert_paths[1])) #AWS_chain.crt

digest_algorithm_oid = ObjectIdentifier(ObjectIdentifier.e_SHA256)
signature_algorithm_oid = ObjectIdentifier(ObjectIdentifier.e_RSA_encryption_PKCS1)

cms_signature = DigitalSignatureField.GenerateCMSSignature(signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid, signature_value, signedAttrs)

in_opts = VerificationOptions(0)
verification_results = digsig_field.Verify(in_opts)
print(verification_results.GetDigestStatusAsString())
print(verification_results.GetDocumentStatusAsString())

doc.SaveCustomSignature(cms_signature, digsig_field, in_outpath)

And the request_signature code:

"""CloudHSM Demo""" 
import os
import base64 
import PyKCS11 
from PyKCS11.LowLevel import CKF_RW_SESSION, CKA_CLASS, CKO_PRIVATE_KEY, CKM_SHA256_RSA_PKCS, CKM_SHA256

import binascii

def request_signature(payload): 
    """ 
    Request the HSM for a signature of the payload. 
    """ 
    # This should be your CU username and password formatted like this 'user_name:password'.
    hsm_credentials = os.getenv('HSM_CREDENTIALS')
    # This will be the data you want to sign.
    session = create_hsm_session() 
 
    private_key = login_hsm_get_key(session, hsm_credentials) 
    signature = sign_payload(session, payload, private_key) 
 
    session.logout()            
    session.closeSession() 
    return signature

def create_hsm_session(): 
    """ 
    Creates a HSM session and returns the session. 
 
    :return: The HSM session. 
    """ 

    # Load PKCS#11 LIB. 
    pkcs11 = PyKCS11.PyKCS11Lib() 
    pkcs11.load('/opt/cloudhsm/lib/libcloudhsm_pkcs11.so') 
    
    try:
        # Get first slot as CloudHSM only has one slot. 
        slot = pkcs11.getSlotList()[0] 
        # Create Session. 
        session = pkcs11.openSession(slot, CKF_RW_SESSION) 
 
        return session     

    except PyKCS11.PyKCS11Error as exc: 
        return {"ERROR": f"PKCS#11 Error when creating session.{str(exc)}"} 

def login_hsm_get_key(session, credentials): 
    """" 
    Logs in to HSM with credentials returns users private key. 
 
    :param session: The HSM session. 
    :param credentials: The credentials to login to the HSM. 
 
    :return: The users private key. 
    """      
    try: 
        # Login to HSM. 
        session.login(credentials) 
 
        # Get private key for user. 
        private_key = session.findObjects([(CKA_CLASS, CKO_PRIVATE_KEY)])[0] 
 
        return private_key   

    except PyKCS11.PyKCS11Error: 
        return {"ERROR": "PKCS#11 Error when logging in and getting private key."} 

def sign_payload(session, payload, private_key): 
    """     
    Signs a payload and returns a signed payload. 
 
    :param session: The HSM session. 
    :param payload: The payload to sign. 
    :param private_key: The private key to use. 
 
    :return: The signature. 
    """ 
    try: 
        # You can change this to desired mechanism such as 'CKM_SHA1_RSA_PKCS'.
 
        mechanism = PyKCS11.Mechanism(CKM_SHA256_RSA_PKCS, None) 
        signature = session.sign(private_key, payload, mechanism) 
 
        return signature
     
    except PyKCS11.PyKCS11Error as exc: 
        return {"ERROR": f"PKCS#11 Error when signing payload: {str(exc)}"}

I am relatively new to signing PDFs/the structure of digital signatures.

I am expecting AdobePDF to return “Unable to verify the signer” because we do not have AATL compatible certs yet, but I am only getting the following message from Adobe saying the certificate is corrupted/incomplete:
image
And the following output from the program run:

No digest status to report.
SignatureHandler reports corruption in the Contents of the digital signature.

Am I missing something glaring here?

Thank you for contacting us about this. I see that you are attempting to verify the signature before it has been written into the PDF. Could you please remove the verification code and let us know if this resolved the corruption issue?

Specifically, please comment out the below code:

in_opts = VerificationOptions(0)
verification_results = digsig_field.Verify(in_opts)
print(verification_results.GetDigestStatusAsString())
print(verification_results.GetDocumentStatusAsString())

If this does not solve the issue, could you please try to use your HSM credentials and try and sign the same file using Adobe Reader? This is just to ensure if the same issue is present in Adobe Reader.

I attempted with the verification code commented out and had the same results.

image

I am not sure how to sign a file using Adobe Reader and AWS’s CloudHSM - I was just checking the cert validity with Adobe Acrobat. I think I may have problems trying to directly interface with our HSM through Adobe Reader because our CloudHSM instance is locked down pretty heavily

Hi Shakthi - I’m still pretty stuck on this, is there anything else I may be able to try?

Update: I was using the wrong key to sign with, but it’s now showing the document has been altered since signing
image

is CKM_SHA256_RSA_PKCS the correct mechanism to use to sign on the HSM, to correspond with ObjectIdentifier.e_RSA_encryption_PKCS1 for signature_algorithm_oid?

For anyone coming to this question I’ve got it solved!

If you’re using CKM_SHA256_RSA_PKCS as the mechanism to sign in the HSM, you have to pass signedAttrs to be signed, not signedAttrs_digest because the CKM_SHA256_RSA_PKCS mechanism digests as SHA256 before signing, and passing signedAttrs_digest would be double digesting resulting in a different hash being attached to the doc.

1 Like