简体   繁体   中英

Sign certificate request with smartcard private key in PKCS#11

I would like to sign a certificate request with a smartcard's private key with PKCS#11. I do mean signing the CSR itself and not writing a certificate in response.

So I am using OpenSSL to create the X509 certificate request, and I want to use PKCS#11 and C_Sign to create the signature. How can I do that?

This is what I have currently, but when I try to generate a certificate from this request with OpenSSL CLI, it says the signature doesn't match so I must be doing something wrong. I'm not sure what to pass C_Sign —right now I've tried the output of i2d_X509_REQ()— and how to set the signature back in X509_REQ once it's been created (I've tried building an ASN1_BIT_STRING object).

Note: this is not a duplicate of this question because this one is for a certificate and works for an old version of OpenSSL's API. Although, I have tried to use the answer by manually exposing the internals of the X509_REQ structure (see last code block).

X509_REQ makeCSR() {
    /* Create OpenSSL EVP_PKEY from exported public key components */
    RSA* openssl_rsa = RSA_new();
    BIGNUM* bn_modulus = BN_bin2bn(modulus.data(), (int) modulus.size(), nullptr);
    BIGNUM* bn_public_exponent = BN_bin2bn(public_exponent.data(), (int) public_exponent.size(), nullptr);
    int success = RSA_set0_key(openssl_rsa, bn_modulus, bn_public_exponent, nullptr);
    EVP_PKEY* evp_pkey = EVP_PKEY_new();

    /* Add public key to certificate request */
    EVP_PKEY_assign(evp_pkey, EVP_PKEY_RSA, openssl_rsa);
    X509_REQ* request = X509_REQ_new();
    X509_REQ_set_pubkey(request, evp_pkey);

    /* Set certificate request attributes */
    // ...

    /* Sign certificate request with smart card */
    unsigned char* buffer { nullptr };
    int size = i2d_X509_REQ(request, &buffer);
    std::vector<unsigned char> der_encoded_request(buffer, buffer + size);
    std::vector<unsigned char> signature = smartcard->signCertificateRequest(der_encoded_request);

    /* Build signature object */
    ASN1_BIT_STRING* asn1_signature = ASN1_BIT_STRING_new();
    ASN1_BIT_STRING_set(asn1_signature, signature.data(), (int) signature.size());
    asn1_signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
    asn1_signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
    X509_ALGOR* x509_algor = X509_ALGOR_new();
    ASN1_OBJECT* a = OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS));
    X509_ALGOR_set0(x509_algor, a, V_ASN1_NULL, nullptr);

    /* Add signature to X509_REQ */
    X509_REQ_set1_signature_algo(request, x509_algor);
    X509_REQ_set0_signature(request, asn1_signature);
    return request;
}

std::vector<unsigned char> signCertificateRequest(std::vector<unsigned char>& certificate_request)
{
    CK_MECHANISM mechanism = { CKM_SHA1_RSA_PKCS, nullptr, 0 };
    auto result = s_pkcs11->fn->C_SignInit(m_session_handle, &mechanism, private_key_handle);

    unsigned long signature_length { 0 };
    result = pkcs11->C_Sign(m_session_handle,
                                  certificate_request.data(),
                                  (unsigned long) certificate_request.size(),
                                  nullptr,
                                  &signature_length);
    std::vector<unsigned char> signature(signature_length);
    result = pkcs11->C_Sign(m_session_handle,
                                  certificate_request.data(),
                                  (unsigned long) certificate_request.size(),
                                  signature.data(),
                                  &signature_length);
    return signature;
}

I've also tried exposing the internals of X509_REQ and passing the output of i2d_X509_REQ_INFO(&request->req_info, &buffer) to C_Sign; or using ASN1_item_i2d() ; and also copying the signature output directly to request->signature->data.

    request->req_info.enc.modified = 1;
    X509_ALGOR_set0(&request->sig_alg,
                    OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS)),
                    V_ASN1_NULL,
                    nullptr);
    unsigned char* certDerBuf = NULL;
    const auto certDerLen = ASN1_item_i2d(ASN1_VALUE*) &request->req_info,
                                          &certDerBuf,
                                          ASN1_ITEM_rptr(X509_REQ_INFO));
    std::vector<unsigned char> certDerVec(certDerBuf, certDerBuf + certDerLen);
    std::vector<unsigned char> signature = smartcard->signCertificateRequest(certDerVec);
    request->signature->data = (unsigned char*) OPENSSL_malloc(signature.size());
    request->signature->length = (int) signature.size();
    request->signature->data = signature.data();

I was able to resolve my problem in the end; this is my complete solution. Error handling and PKCS#11 session handling have been omitted. I use a unique_ptr for managing OpenSSL resources. I had to expose OpenSSL's X509_REQ and X509_REQ_INFO structures because I couldn't find accessors for req_info in OpenSSL 1.1.1h.

// OpenSSL internal definitions
using CRYPTO_REF_COUNT = int;
struct X509_req_info_st {
    ASN1_ENCODING enc;     /* cached encoding of signed part */
    ASN1_INTEGER* version; /* version, defaults to v1(0) so can be NULL */
    X509_NAME* subject;    /* certificate request DN */
    X509_PUBKEY* pubkey;   /* public key of request */
    /*
     * Zero or more attributes.
     * NB: although attributes is a mandatory field some broken
     * encodings omit it so this may be NULL in that case.
     */
    STACK_OF(X509_ATTRIBUTE) * attributes;
};
struct X509_req_st {
    X509_REQ_INFO req_info;     /* signed certificate request data */
    X509_ALGOR sig_alg;         /* signature algorithm */
    ASN1_BIT_STRING* signature; /* signature */
    CRYPTO_REF_COUNT references;
    CRYPTO_RWLOCK* lock;
};

template<typename T>
using AutoDeletedPtr = std::unique_ptr<T, std::function<void(T*)>>;

void makeCSR(const std::vector<unsigned char>& modulus,
             const std::vector<unsigned char>& public_exponent)
{
    /* Create OpenSSL EVP_PKEY from exported public key components */
    auto* openssl_rsa = RSA_new();
    auto bn_modulus = BN_bin2bn(modulus.data(), static_cast<int>(modulus.size()), nullptr);
    auto bn_public_exponent = BN_bin2bn(public_exponent.data(),
                                        static_cast<int>(public_exponent.size()),
                                        nullptr);
    auto success = RSA_set0_key(openssl_rsa, bn_modulus, bn_public_exponent, nullptr);

    auto key_file = AutoDeletedPtr<BIO>(BIO_new_file("key.pem", "wb"), BIO_free);
    PEM_write_bio_RSA_PUBKEY(key_file.get(), openssl_rsa);

    auto evp_pkey = AutoDeletedPtr<EVP_PKEY>(EVP_PKEY_new(), EVP_PKEY_free);

    /* Add public key to certificate request */
    EVP_PKEY_assign(evp_pkey.get(), EVP_PKEY_RSA, openssl_rsa);

    auto request = AutoDeletedPtr<X509_REQ>(X509_REQ_new(), X509_REQ_free);

    X509_REQ_set_pubkey(request.get(), evp_pkey.get());

    /* Set certificate request attributes */
    // ...
    
    /* Sign certificate request with smart card */
    unsigned char* buffer { nullptr };
    auto size = i2d_X509_REQ_INFO(&request->req_info, &buffer);
    std::vector<unsigned char> der_encoded_request(buffer, buffer + size);

    auto signature = signCertificateRequest(der_encoded_request);

    auto asn1_signature = ASN1_BIT_STRING_new();

    ASN1_BIT_STRING_set(asn1_signature, signature.data(), static_cast<int>(signature.size()));
    auto x509_algor = AutoDeletedPtr<X509_ALGOR>(X509_ALGOR_new(), X509_ALGOR_free);
    auto* a = OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS));
    auto algor_set_result = X509_ALGOR_set0(x509_algor.get(), a, V_ASN1_NULL, nullptr);

    X509_REQ_set1_signature_algo(request.get(), x509_algor.get());
    X509_REQ_set0_signature(request.get(), asn1_signature);
    request->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
    request->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;

    // Do what you want with the request
}

std::vector<unsigned char> signCertificateRequest(std::vector<unsigned char>& certificate_request)
{
    // You'll need an open session and be logged in

    CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY;
    CK_KEY_TYPE private_key_type = CKK_RSA;
    std::array<unsigned char, 4> id = "myid";
    std::vector<CK_ATTRIBUTE> private_key_template = {
        { CKA_CLASS, &private_key_class, sizeof(private_key_class) },
        { CKA_KEY_TYPE, &private_key_type, sizeof(private_key_type) },
        { CKA_ID, id.data(), static_cast<long>(id.size()) },
    };

    auto result = s_pkcs11->fn->C_FindObjectsInit(m_session_handle,
                                                  private_key_template.data(),
                                                  static_cast<unsigned long>(private_key_template.size()));

    CK_OBJECT_HANDLE private_key_handle { 0 };
    unsigned long object_count { 0 };
    result = s_pkcs11->fn->C_FindObjects(m_session_handle, &private_key_handle, 1, &object_count);
    result = s_pkcs11->fn->C_FindObjectsFinal(m_session_handle);

    CK_MECHANISM mechanism = { CKM_SHA1_RSA_PKCS, nullptr, 0 };
    auto result = s_pkcs11->fn->C_SignInit(m_session_handle, &mechanism, private_key_handle);

    unsigned long signature_length { 0 };
    result = s_pkcs11->fn->C_Sign(m_session_handle,
                                  certificate_request.data(),
                                  static_cast<unsigned long>(certificate_request.size()),
                                  nullptr,
                                  &signature_length);

    std::vector<unsigned char> signature(signature_length);
    result = s_pkcs11->fn->C_Sign(m_session_handle,
                                  certificate_request.data(),
                                  static_cast<unsigned long>(certificate_request.size()),
                                  signature.data(),
                                  &signature_length);
    return signature;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM