Use of separated certificate and private key might be confusing without understanding how parts or CryptoAPI are related one to another. Apparently, CryptSignMessage
and friends require private key in order to create a digital signature.
It is not a problem when private key resides right in the signing certificate (such as, for example, imported with PFXImportCertStore
):
CRYPT_SIGN_MESSAGE_PARA Parameters;
ZeroMemory(&Parameters, sizeof Parameters);
Parameters.cbSize = sizeof Parameters;
Parameters.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
Parameters.pSigningCert = CertificateContext; // <<--- Certificate with Private Key
// ...
Things get more complicated when the certificate does not contain the private key. API’s error is CRYPT_E_NO_KEY_PROPERTY
which is described as follows:
“The
pSigningCert
in*pSignPara
does not have aCERT_KEY_PROV_INFO_PROP_ID
orCERT_KEY_CONTEXT_PROP_ID
property.”
CERT_KEY_PROV_INFO_PROP_ID
is a property to be defined with certificate context and it expects a structure of type CRYPT_KEY_PROV_INFO
. The other one, CERT_KEY_CONTEXT_PROP_ID
takes a CERT_KEY_CONTEXT
structure.
Even though described as “The CRYPT_KEY_PROV_INFO
structure contains information about a key container within a cryptographic service provider (CSP).” and “The CERT_KEY_CONTEXT
structure contains data associated with a CERT_KEY_CONTEXT_PROP_ID
property.” respectively, it is not well clear to see where the key is attached exactly.
The missing part is that the private key is not something to be provided explicitly. Instead, the key is to be pre-loaded into provider and then this provider is associated with CERT_KEY_CONTEXT
structure, which goes as a property value, which in turn is added to signing certificate context, whcih finally gets used for creating a digital signature.
__E(CryptStringToBinary(sPrivateKeyBlob, (DWORD) wcslen(sPrivateKeyBlob), CRYPT_STRING_BASE64_ANY, pnData, &nDataSize, NULL, NULL));
// ...
CCryptProvider Provider;
__E(Provider.AcquireContext(NULL, NULL, PROV_RSA_FULL, 0));
CCryptKey Key;
Key.Attach(Provider.ImportKey(pnData, nDataSize));
__E(Key);
// ...
CERT_KEY_CONTEXT KeyContext;
ZeroMemory(&KeyContext, sizeof KeyContext);
KeyContext.cbSize = sizeof KeyContext;
KeyContext.hCryptProv = Provider; // <<--- Here we go, ooh
KeyContext.dwKeySpec = AT_SIGNATURE;
CertificateContext.SetProperty(CERT_KEY_CONTEXT_PROP_ID, &KeyContext);