I have the following C++ code (based on Microsoft API examples), it uses message signing and encryption API to produce PKCS#7 message.
There is a hardware token with certificate inserted (Yubikey) which requires a PIN code for the signing operation.
I want to disable the standard Windows pin popup and provide my own, so there is a call to CryptSetProvParam
with a pin code.
The issue is that this call has no effect: I am still prompted with pin code.
If I add CertSetCertificateContextProperty
call afterwards and set the key provider handle then nothing works anymore, for example CryptMsgCalculateEncodedLength
returns 0x80090027.
If I comment out CertSetCertificateContextProperty
then all operations are working but the pin code popup is still shown.
#pragma comment(lib, "crypt32.lib")
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
void MyHandleError(char* s);
void main(void)
{
//-------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Declare and initialize variables.
BYTE* pbContent = (BYTE*)"Security is our only business.";
// a byte pointer
DWORD cbContent = strlen((char*)pbContent) + 1;
// the size of the message
HCERTSTORE hStoreHandle;
HCRYPTPROV hCryptProv;
PCCERT_CONTEXT pSignerCert; // signer's certificate
PCCERT_CONTEXT pRecipCert; // receiver's certificate
LPWSTR pswzRecipientName = L"myrecipient";
LPWSTR pswzCertSubject = L"mysubject";
PCERT_INFO RecipCertArray[1];
DWORD ContentEncryptAlgSize;
CRYPT_ALGORITHM_IDENTIFIER ContentEncryptAlgorithm;
CMSG_ENVELOPED_ENCODE_INFO EnvelopedEncodeInfo;
DWORD cbEncodedBlob;
BYTE* pbEncodedBlob;
DWORD cbSignedBlob;
BYTE* pbSignedBlob;
HCRYPTMSG hMsg;
DWORD HashAlgSize;
CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo;
CERT_BLOB SignerCertBlob;
CERT_BLOB SignerCertBlobArray[1];
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfoArray[1];
CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo;
DWORD dwKeySpec = 0;
HCRYPTPROV rsaCryptProv = NULL;
//-------------------------------------------------------------------
// Begin processing. Display the original message.
printf("The original message => %s\n", pbContent);
//-------------------------------------------------------------------
// Acquire a cryptographic provider.
if (CryptAcquireContext(
&hCryptProv, // address for handle to be returned
NULL, // use the current user's logon name
NULL, // use the default provider
PROV_RSA_FULL, // provider type
0)) // zero allows access to private keys
{
printf("Context CSP acquired. \n");
}
else
{
if (GetLastError() == NTE_BAD_KEYSET)
{
printf("A Usable private key was not found \n");
printf("in the default key container. Either a \n");
printf("private key must be generated in that container \n");
printf("or CryptAquireCertificatePrivateKey can be used \n");
printf("to gain access to the needed private key.");
}
MyHandleError("CryptAcquireContext failed.");
}
//-------------------------------------------------------------------
// Open the My system certificate store.
if (hStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
L"MY"))
{
printf("The MY system store is open. \n");
}
else
{
MyHandleError("Error getting store handle.");
}
//-------------------------------------------------------------------
// Get the signer's certificate. This certificate must be in the
// My store, and its private key must be available.
if (pSignerCert = CertFindCertificateInStore(
hStoreHandle,
MY_ENCODING_TYPE,
0,
CERT_FIND_SUBJECT_STR,
pswzCertSubject,
NULL))
{
printf("Found certificate for %S.\n", pswzCertSubject);
}
else
{
MyHandleError("Signer certificate not found.");
}
if (!CryptAcquireCertificatePrivateKey(pSignerCert, CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_SILENT_FLAG, NULL, &rsaCryptProv, &dwKeySpec, NULL))
{
MyHandleError("Cannot acquire private key.");
}
// does not seem to have any effect
if (!CryptSetProvParam(rsaCryptProv, dwKeySpec == AT_KEYEXCHANGE ? PP_KEYEXCHANGE_PIN : PP_SIGNATURE_PIN, (LPBYTE)"123456", 0))
{
MyHandleError("Cannot set key pin.");
}
// causes all subsequent operations to fail
if (!CertSetCertificateContextProperty(pSignerCert, CERT_KEY_PROV_HANDLE_PROP_ID, 0, (const void*)rsaCryptProv))
{
MyHandleError("Cannot set cert property.");
}
//-------------------------------------------------------------------
// Initialize the algorithm identifier structure.
HashAlgSize = sizeof(HashAlgorithm);
memset(&HashAlgorithm, 0, HashAlgSize); // initialize to zero
HashAlgorithm.pszObjId = szOID_RSA_SHA256RSA; // initialize the
// necessary member
//-------------------------------------------------------------------
// Initialize the CMSG_SIGNER_ENCODE_INFO structure.
memset(&SignerEncodeInfo, 0, sizeof(CMSG_SIGNER_ENCODE_INFO));
SignerEncodeInfo.cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
SignerEncodeInfo.pCertInfo = pSignerCert->pCertInfo;
SignerEncodeInfo.hCryptProv = rsaCryptProv;
SignerEncodeInfo.dwKeySpec = dwKeySpec;
SignerEncodeInfo.HashAlgorithm = HashAlgorithm;
SignerEncodeInfo.pvHashAuxInfo = NULL;
//-------------------------------------------------------------------
// Create an array of one.
// Note: The current program is set up for only a single signer.
SignerEncodeInfoArray[0] = SignerEncodeInfo;
//-------------------------------------------------------------------
// Initialize the CMSG_SIGNED_ENCODE_INFO structure.
SignerCertBlob.cbData = pSignerCert->cbCertEncoded;
SignerCertBlob.pbData = pSignerCert->pbCertEncoded;
//-------------------------------------------------------------------
// Initialize the array of one CertBlob.
SignerCertBlobArray[0] = SignerCertBlob;
memset(&SignedMsgEncodeInfo, 0, sizeof(CMSG_SIGNED_ENCODE_INFO));
SignedMsgEncodeInfo.cbSize = sizeof(CMSG_SIGNED_ENCODE_INFO);
SignedMsgEncodeInfo.cSigners = 1;
SignedMsgEncodeInfo.rgSigners = SignerEncodeInfoArray;
SignedMsgEncodeInfo.cCertEncoded = 1;
SignedMsgEncodeInfo.rgCertEncoded = SignerCertBlobArray;
SignedMsgEncodeInfo.rgCrlEncoded = NULL;
//-------------------------------------------------------------------
// Get the size of the encoded, signed message BLOB.
if (cbSignedBlob = CryptMsgCalculateEncodedLength(
MY_ENCODING_TYPE, // message encoding type
0, // flags
CMSG_SIGNED, // message type
&SignedMsgEncodeInfo, // pointer to structure
NULL, // inner content OID
cbContent)) // size of content
{
printf("%d, the length of data calculated. \n", cbSignedBlob);
}
else
{
if (GetLastError() == NTE_BAD_KEYSET)
{
printf("A Usable private key was not found \n");
printf("in the default key container. Either a \n");
printf("private key must be generated in that container \n");
printf("or CryptAquireCertificatePRivateKey can be used \n");
printf("to gain access to the needed private key.");
}
MyHandleError("Getting cbSignedBlob length failed.");
}
//-------------------------------------------------------------------
// Allocate memory for the encoded BLOB.
if (pbSignedBlob = (BYTE*)malloc(cbSignedBlob))
{
printf("Memory has been allocated for the signed message. \n");
}
else
{
MyHandleError("Memory allocation failed.");
}
//-------------------------------------------------------------------
// Open a message to encode.
if (hMsg = CryptMsgOpenToEncode(
MY_ENCODING_TYPE, // encoding type
0, // flags
CMSG_SIGNED, // message type
&SignedMsgEncodeInfo, // pointer to structure
NULL, // inner content OID
NULL)) // stream information (not used)
{
printf("The message to be encoded has been opened. \n");
}
else
{
MyHandleError("OpenToEncode failed.");
}
//-------------------------------------------------------------------
// Update the message with the data.
if (CryptMsgUpdate(
hMsg, // handle to the message
pbContent, // pointer to the content
cbContent, // size of the content
TRUE)) // last call
{
printf("Content has been added to the encoded message. \n");
}
else
{
MyHandleError("MsgUpdate failed.");
}
//-------------------------------------------------------------------
// Get the resulting message.
if (CryptMsgGetParam(
hMsg, // handle to the message
CMSG_CONTENT_PARAM, // parameter type
0, // index
pbSignedBlob, // pointer to the BLOB
&cbSignedBlob)) // size of the BLOB
{
printf("Message encoded successfully. \n");
FILE* fp = fopen("signed.p7m", "wb");
fwrite(pbSignedBlob, cbSignedBlob, 1, fp);
fclose(fp);
}
else
{
MyHandleError("MsgGetParam failed.");
}
//-------------------------------------------------------------------
// pbSignedBlob now points to the encoded, signed content.
//-------------------------------------------------------------------
// Get a pointer to the recipient certificate.
// For this program, the recipient's certificate must also be in the
// My store. At this point, only the recipient's public key is needed.
// To open the enveloped message, however, the recipient's
// private key must also be available.
if (pRecipCert = CertFindCertificateInStore(
hStoreHandle,
MY_ENCODING_TYPE, // use X509_ASN_ENCODING
0, // no dwFlags needed
CERT_FIND_SUBJECT_STR, // find a certificate with a
// subject that matches the
// string in the next parameter
pswzRecipientName, // the Unicode string to be found
// in a certificate's subject
NULL)) // NULL for the first call to the
// function
// in all subsequent
// calls, it is the last pointer
// returned by the function
{
printf("Certificate for %S found. \n", pswzRecipientName);
}
else
{
MyHandleError("Could not find the countersigner's "
"certificate.");
}
//-------------------------------------------------------------------
// Initialize the first element of the array of CERT_INFOs.
// In this example, there is only a single recipient.
RecipCertArray[0] = pRecipCert->pCertInfo;
//-------------------------------------------------------------------
// Initialize the symmetric-encryption algorithm identifier
// structure.
ContentEncryptAlgSize = sizeof(ContentEncryptAlgorithm);
memset(&ContentEncryptAlgorithm,
0,
ContentEncryptAlgSize); // initialize to zero
//-------------------------------------------------------------------
// Initialize the necessary members. This particular OID does not
// need any parameters. Some OIDs, however, will require that
// the other members be initialized.
ContentEncryptAlgorithm.pszObjId = szOID_RSA_DES_EDE3_CBC;
//-------------------------------------------------------------------
// Initialize the CMSG_ENVELOPED_ENCODE_INFO structure.
memset(&EnvelopedEncodeInfo,
0,
sizeof(CMSG_ENVELOPED_ENCODE_INFO));
EnvelopedEncodeInfo.cbSize = sizeof(CMSG_ENVELOPED_ENCODE_INFO);
EnvelopedEncodeInfo.hCryptProv = hCryptProv;
EnvelopedEncodeInfo.ContentEncryptionAlgorithm =
ContentEncryptAlgorithm;
EnvelopedEncodeInfo.pvEncryptionAuxInfo = NULL;
EnvelopedEncodeInfo.cRecipients = 1;
EnvelopedEncodeInfo.rgpRecipients = RecipCertArray;
//-------------------------------------------------------------------
// Get the size of the encoded message BLOB.
if (cbEncodedBlob = CryptMsgCalculateEncodedLength(
MY_ENCODING_TYPE, // message encoding type
0, // flags
CMSG_ENVELOPED, // message type
&EnvelopedEncodeInfo, // pointer to structure
NULL, // inner content OID
cbSignedBlob)) // size of content
{
printf("Length of the encoded BLOB will be %d.\n", cbEncodedBlob);
}
else
{
MyHandleError("Getting enveloped cbEncodedBlob length failed.");
}
//--------------------------------------------------------------------
// Allocate memory for the encoded BLOB.
if (pbEncodedBlob = (BYTE*)malloc(cbEncodedBlob))
{
printf("Memory has been allocated for the BLOB. \n");
}
else
{
MyHandleError("Enveloped malloc operation failed.");
}
//-------------------------------------------------------------------
// Open a message to encode.
if (hMsg = CryptMsgOpenToEncode(
MY_ENCODING_TYPE, // encoding type
0, // flags
CMSG_ENVELOPED, // message type
&EnvelopedEncodeInfo, // pointer to structure
NULL, // inner content OID
NULL)) // stream information (not used)
{
printf("The message to encode is open. \n");
}
else
{
MyHandleError("Enveloped OpenToEncode failed.");
}
//-------------------------------------------------------------------
// Update the message with the data.
if (CryptMsgUpdate(
hMsg, // handle to the message
pbSignedBlob, // pointer to the signed data BLOB
cbSignedBlob, // size of the data BLOB
TRUE)) // last call
{
printf("The signed BLOB has been added to the message. \n");
}
else
{
MyHandleError("Enveloped MsgUpdate failed.");
}
//-------------------------------------------------------------------
// Get the resulting message.
if (CryptMsgGetParam(
hMsg, // handle to the message
CMSG_CONTENT_PARAM, // parameter type
0, // index
pbEncodedBlob, // pointer to the enveloped,
// signed data BLOB
&cbEncodedBlob)) // size of the BLOB
{
printf("Enveloped message encoded successfully. \n");
FILE* fp = fopen("encoded.p7m", "wb");
fwrite(pbEncodedBlob, cbEncodedBlob, 1, fp);
fclose(fp);
}
else
{
MyHandleError("Enveloped MsgGetParam failed.");
}
//-------------------------------------------------------------------
// Clean up.
CertFreeCertificateContext(pRecipCert);
if (CertCloseStore(
hStoreHandle,
CERT_CLOSE_STORE_CHECK_FLAG))
{
printf("The certificate store closed without a certificate "
"left open. \n");
}
else
{
printf("The store closed but a certificate was still open. \n");
}
if (hMsg)
CryptMsgClose(hMsg);
if (hCryptProv)
CryptReleaseContext(hCryptProv, 0);
} // end main
//-------------------------------------------------------------------
// This example uses the function MyHandleError, a simple error
// handling function to print an error message and exit
// the program.
// For most applications, replace this function with one
// that does more extensive error reporting.
void MyHandleError(char* s)
{
fprintf(stderr, "An error occurred in running the program. \n");
fprintf(stderr, "%s\n", s);
fprintf(stderr, "Error number %x.\n", GetLastError());
fprintf(stderr, "Program terminating. \n");
exit(1);
} // end MyHandleError
Found an answer and posting it here in case anyone needs it. The trick is to use CNG API instead of legacy crypto API:
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
void MyHandleError(const char* s);
int main()
{
BYTE* pbContent = (BYTE*)"Security is our only business.";
DWORD cbContent = strlen((char*)pbContent) + 1;
LPCWSTR pswzRecipientName = L"recipient";
LPCWSTR pswzCertSubject = L"signer";
HCERTSTORE hStoreHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, L"my");
if (hStoreHandle != NULL)
{
printf("The MY system store is open. \n");
}
else
{
MyHandleError("Error getting store handle.");
}
PCCERT_CONTEXT pSignerCert = CertFindCertificateInStore(hStoreHandle, MY_ENCODING_TYPE, 0, CERT_FIND_SUBJECT_STR, pswzCertSubject, NULL);
if (pSignerCert != NULL)
{
printf("Found certificate for %S.\n", pswzCertSubject);
}
else
{
MyHandleError("Signer certificate not found.");
}
PCCERT_CONTEXT pRecipCert = CertFindCertificateInStore(hStoreHandle, MY_ENCODING_TYPE, 0, CERT_FIND_SUBJECT_STR, pswzRecipientName, NULL);
if (pRecipCert != NULL)
{
printf("Certificate for %S found. \n", pswzRecipientName);
}
else
{
MyHandleError("Could not find the countersigner's "
"certificate.");
}
NCRYPT_HANDLE rsaKey;
DWORD dwKeySpec = 0;
if (!CryptAcquireCertificatePrivateKey(pSignerCert, CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, NULL, &rsaKey, &dwKeySpec, NULL))
{
MyHandleError("Cannot acquire private key.");
}
if (NCryptSetProperty(rsaKey, NCRYPT_PIN_PROPERTY, (BYTE*)L"123456", 6, 0) != ERROR_SUCCESS)
{
MyHandleError("NCryptSetProperty failed.");
}
CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
memset(&HashAlgorithm, 0, sizeof(HashAlgorithm));
HashAlgorithm.pszObjId = szOID_RSA_SHA256RSA;
CRYPT_SIGN_MESSAGE_PARA SignParam;
memset(&SignParam, 0, sizeof(SignParam));
SignParam.cbSize = sizeof(SignParam);
SignParam.dwMsgEncodingType = MY_ENCODING_TYPE;
SignParam.pSigningCert = pSignerCert;
SignParam.cMsgCert = 1;
SignParam.rgpMsgCert = &pSignerCert;
SignParam.HashAlgorithm = HashAlgorithm;
SignParam.dwFlags = CRYPT_MESSAGE_SILENT_KEYSET_FLAG;
CRYPT_ALGORITHM_IDENTIFIER CryptAlg;
memset(&CryptAlg, 0, sizeof(CryptAlg));
CryptAlg.pszObjId = szOID_RSA_DES_EDE3_CBC;
CRYPT_ENCRYPT_MESSAGE_PARA EncryptParam;
memset(&EncryptParam, 0, sizeof(EncryptParam));
EncryptParam.cbSize = sizeof(EncryptParam);
EncryptParam.dwMsgEncodingType = MY_ENCODING_TYPE;
EncryptParam.ContentEncryptionAlgorithm = CryptAlg;
DWORD cbEncodedBlob = 0;
if (!CryptSignAndEncryptMessage(&SignParam, &EncryptParam, 1, &pRecipCert, pbContent, cbContent, NULL, &cbEncodedBlob))
{
MyHandleError("CryptSignAndEncrypteMessage failed.");
}
BYTE* pbEncodedBlob = (BYTE*)malloc(cbEncodedBlob);
if (!CryptSignAndEncryptMessage(&SignParam, &EncryptParam, 1, &pRecipCert, pbContent, cbContent, pbEncodedBlob, &cbEncodedBlob))
{
MyHandleError("CryptSignAndEncrypteMessage failed.");
}
else
{
printf("Message signed and encrypted successfully. \n");
FILE* fp = fopen("signed-encrypted.p7m", "wb");
fwrite(pbEncodedBlob, cbEncodedBlob, 1, fp);
fclose(fp);
}
free(pbEncodedBlob);
CertFreeCertificateContext(pSignerCert);
CertFreeCertificateContext(pRecipCert);
CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG);
return 0;
}
void MyHandleError(const char* s)
{
fprintf(stderr, "An error occurred in running the program. \n");
fprintf(stderr, "%s\n", s);
fprintf(stderr, "Error number %x.\n", GetLastError());
fprintf(stderr, "Program terminating. \n");
exit(1);
}
User contributions licensed under CC BY-SA 3.0