Search code examples
opensslcertificatesigncsrpkcs#11

Sign certificate in PKCS#11


To sign certificate in OpenSSL I use X509_sign() function by feeding it with a request (as X509_REQ*), a signing key and a digest.

Now I have my signing key stored in HSM, so I can't extract it to sign the certificate. Unfortunately PKCS#11 does not provide an equivalent to X509_sign(). All it has is C_Sign() / C_SignUpdate() / C_SignFinal() family of functions which operate on raw data, not on certificates.

Can someone help me with sample C/C++ code how to use PKCS#11 to sign a certificate created with OpenSSL?


Solution

  • The code below signs the given X509* structure within HSM. Error handling is omitted for the sake of clarity.

    void signCertInHsm(X509* x509, unsigned long pkcs11SigningAlgo, CK_FUNCTION_LIST_PTR p11, CK_SESSION_HANDLE p11session, CK_OBJECT_HANDLE pkcs11PrivKeyHandle)
    {
        x509->cert_info->enc.modified = 1;
    
        // set signature algorithm in the certificate
        if (x509->cert_info->signature)
        {
            const int signingAlgoNid = pkcs11SignatureAlgorithmToNid(pkcs11SigningAlgo);
            X509_ALGOR_set0(x509->cert_info->signature, OBJ_nid2obj(signingAlgoNid), V_ASN1_NULL, NULL);
        }
        if (x509->sig_alg)
        {
            const int signingAlgoNid = pkcs11SignatureAlgorithmToNid(pkcs11SigningAlgo);
            X509_ALGOR_set0(x509->sig_alg, OBJ_nid2obj(signingAlgoNid), V_ASN1_NULL, NULL);
        }
    
        // DER-encode certificate
        unsigned char *certDerBuf = NULL;
        const size_t certDerLen = ASN1_item_i2d((ASN1_VALUE*)x509->cert_info, &certDerBuf, ASN1_ITEM_rptr(X509_CINF));
    
        CK_MECHANISM mechanism = { pkcs11SigningAlgo, NULL_PTR, 0 };
        p11->C_SignInit(p11session, &mechanism, pkcs11PrivKeyHandle);
    
        // determine signature size
        CK_ULONG signatureSize = 0;
        p11->C_Sign(p11session, certDerBuf, certDerLen, NULL, &signatureSize);
    
        // sign
        if (x509->signature->data)
            OPENSSL_free(x509->signature->data);
        x509->signature->data = (unsigned char*)OPENSSL_malloc(signatureSize);
        x509->signature->length = signatureSize;
        p11->C_Sign(p11session, certDerBuf, certDerLen, x509->signature->data, &signatureSize);
    
        x509->signature->flags&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07);
        x509->signature->flags|=ASN1_STRING_FLAG_BITS_LEFT;
    
        OPENSSL_free(certDerBuf);
    }
    
    int pkcs11SignatureAlgorithmToNid(unsigned long algo)
    {
        switch(algo)
        {
        case CKM_SHA1_RSA_PKCS: return NID_sha1WithRSAEncryption;
        case CKM_SHA256_RSA_PKCS: return NID_sha256WithRSAEncryption;
        //... add more mappings that your app supports
        default: throw std::invalid_argument("Not supported signature algorithm");
        }
    }