在 PKCS#11 中使用智能卡私钥签署证书请求
Sign certificate request with smartcard private key in PKCS#11
我想通过 PKCS#11 使用智能卡的私钥签署证书请求。我的意思是自己签署 CSR,而不是写证书作为回应。
所以我使用 OpenSSL 创建 X509 证书请求,我想使用 PKCS#11 和 C_Sign 创建签名。我该怎么做?
这是我目前拥有的,但是当我尝试使用 OpenSSL CLI 从此请求生成证书时,它说签名不匹配,所以我一定是做错了什么。我不确定要传递什么 C_Sign — 现在我已经尝试了 i2d_X509_REQ() 的输出 — 以及如何在 X509_REQ 创建签名后将其设置回(我我尝试构建一个 ASN1_BIT_STRING 对象)。
注意:这不是 的副本,因为它用于证书并且适用于旧版本的 OpenSSL API。虽然,我试图通过手动公开 X509_REQ 结构的内部结构(参见最后一个代码块)来使用答案。
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;
}
我也尝试过公开 X509_REQ 的内部结构并将 i2d_X509_REQ_INFO(&request->req_info, &buffer)
的输出传递给 C_Sign;或使用 ASN1_item_i2d()
;并将签名输出直接复制到 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();
我终于解决了我的问题;这是我的完整解决方案。省略了错误处理和 PKCS#11 会话处理。我使用 unique_ptr 来管理 OpenSSL 资源。我不得不公开 OpenSSL 的 X509_REQ 和 X509_REQ_INFO 结构,因为我在 OpenSSL 1.1.1h 中找不到 req_info 的访问器。
// 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;
}
我想通过 PKCS#11 使用智能卡的私钥签署证书请求。我的意思是自己签署 CSR,而不是写证书作为回应。
所以我使用 OpenSSL 创建 X509 证书请求,我想使用 PKCS#11 和 C_Sign 创建签名。我该怎么做?
这是我目前拥有的,但是当我尝试使用 OpenSSL CLI 从此请求生成证书时,它说签名不匹配,所以我一定是做错了什么。我不确定要传递什么 C_Sign — 现在我已经尝试了 i2d_X509_REQ() 的输出 — 以及如何在 X509_REQ 创建签名后将其设置回(我我尝试构建一个 ASN1_BIT_STRING 对象)。
注意:这不是
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;
}
我也尝试过公开 X509_REQ 的内部结构并将 i2d_X509_REQ_INFO(&request->req_info, &buffer)
的输出传递给 C_Sign;或使用 ASN1_item_i2d()
;并将签名输出直接复制到 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();
我终于解决了我的问题;这是我的完整解决方案。省略了错误处理和 PKCS#11 会话处理。我使用 unique_ptr 来管理 OpenSSL 资源。我不得不公开 OpenSSL 的 X509_REQ 和 X509_REQ_INFO 结构,因为我在 OpenSSL 1.1.1h 中找不到 req_info 的访问器。
// 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;
}