How to sign OCSP responses using a private key stored in AWS CloudHSM in Python

0

I am trying to sign my OCSP builder to create a valid OCSP response for client use. However I'm receiving this error when I try to sign my response builder: 'ERROR': "PKCS#11 Error: Unknown format (<class 'cryptography.x509.ocsp.OCSPResponseBuilder'>)

This is because I'm attempting to sign the OCSP builder using PyKCS11 however I can't do that before the OCSP response is built since it's current of object type 'OCSPResponseBuilder', the issue is building the response inherently requires it to be signed by the OCSP private key which is stored on the CloudHSM.

I am using PyKCS11 to read my private key from my AWS CloudHSM, and using Python Cryptography's OCSP library to build my request.

As I understand it, PKCS11 doesn't give me immediate access to the private key file, so I don't understand how to use the private key programmatically in this context. Bellow is my code, I've marked where the error is with double asterisks '**'

def get(pem_certificate, pem_issuer, pem_responder_cert, logger):
    builder = create_response_GOOD(pem_certificate, pem_issuer, pem_responder_cert, logger)
    response = request_signature(builder, logger)

def create_response_GOOD(cert, issuerCert, responderCert, logger):
    logger.log(f"Generating good response...")
    builder = ocsp.OCSPResponseBuilder()
    builder = builder.add_response(
        cert=cert, issuer=issuerCert, algorithm=hashes.SHA256(),
        cert_status=ocsp.OCSPCertStatus.GOOD,
        this_update=datetime.datetime.now(),
        next_update=datetime.datetime.now(),
        revocation_time=None, revocation_reason=None
        ).responder_id(
            ocsp.OCSPResponderEncoding.HASH, responderCert
        )
    logger.log(f"Good response generated...")
    return builder


def request_signature(builder, logger):
    """
    Request the HSM for a signature of the builder.
    """
    hsm_credentials = 'cryptouser:pw12345'

    session = create_hsm_session()
    logger.log(f"Session created: {session}...")

    private_key = login_hsm_get_key(session, hsm_credentials, logger)
    logger.log(f"Private key pulled...")

    signature = sign_builder(session, builder, private_key, logger)
    logger.log(f"Signature generated: {signature}")

    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:
        return {"ERROR": "PKCS#11 Error when creating session."}

def login_hsm_get_key(session, credentials, logger):
    """"
    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.
    """

    cert_LABEL = "issuing ca ocsp"

    try:
        # Login to HSM.
        session.login(credentials)

        # Get private key for user.
        private_key = session.findObjects([(CKA_CLASS, CKO_PRIVATE_KEY),(CKA_LABEL, cert_LABEL)])
        
        logger.log("Found %d objects: %s" % (len(private_key), [x.value() for x in private_key]))

        return private_key[0]
    except PyKCS11.PyKCS11Error:
        return {"ERROR": "PKCS#11 Error when logging in and getting private key."}
    
def sign_builder(session, builder, private_key, logger):
    """
    Signs a builder and returns a signed builder.

    :param session: The HSM session.
    :param builder: The unbuilt payload to sign.
    :param private_key: The private key to use.

    :return: The signature.
    """
    try:
        **mechanism = PyKCS11.Mechanism(CKM_SHA256_RSA_PKCS, None)
        signature = session.sign(private_key, builder, mechanism)**

        return signature

    except PyKCS11.PyKCS11Error as e:
        logger.log(f"PKCS#11 Error: {e}")
        return {"ERROR": f"PKCS#11 Error: {e}"}
harry
asked 4 months ago202 views
1 Answer
0
Accepted Answer

Hello,

As you rightly observed, the error encountered here is expected as session.sign() method in PyKCS11 cannot sign OCSPResponseBuilder object. The session.sign() method only accepts the data of type binary(a string or list/tuple of bytes)[1]. Hence OCSPResponseBuilder object cannot be passed in session.sign() method for signing.

The OCSP method builder.sign(responder_key, hashes.SHA256()) allows signing the builder object however it requires access to the private key[2]. CloudHSM doesn't allow retrieving the private key in plaintext, so you would need to export the privateKey out of HSM and then use it with your builder.sign() method. The exportPrivateKey command in key_mgmt_util exports an asymmetric private key from an HSM to a file. The HSM does not allow direct export of keys in cleartext. The command wraps the private key using an AES wrapping key you specify, decrypts the wrapped bytes, and copies the cleartext private key to a file[3].

You can then use the cleartext private key to create signature using the method builder.sign(responder_key, hashes.SHA256()).

I hope the above information helps. Please let me know if you have any further queries.

References:

[1] PyKCS11 Session.sign(): https://pkcs11wrap.sourceforge.io/api/api.html#PyKCS11.Session.sign

[2] OCSP builder.sign(): https://cryptography.io/en/latest/x509/ocsp/#creating-responses

[3] exportPrivateKey - https://docs.aws.amazon.com/cloudhsm/latest/userguide/key_mgmt_util-exportPrivateKey.html

AWS
SUPPORT ENGINEER
answered 4 months ago
profile picture
EXPERT
reviewed 15 days ago
  • Hi!

    Thank you for your thorough effort explaining this, I'm currently using CloudHSM's SDK 5 platform, I was really hoping it'd remain possible to continue using SDK 5 for my use case. But, as you pointed out, it seems the only method would be using AWS CloudHSM's KMU package in their SDK 3 platform, since key exportation is currently not supported in CloudHSMs latest software as far as I'm aware. Thank you again for your assistance!

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions