The following example code demonstrates the process of encoding, decoding, and decrypting an enveloped message that has a data inner-content data type.
//=============================================================
// EXAMPLE CODE FOR CREATING AN ENVELOPED MESSAGE.
//=============================================================
// Use this constant definition (or one similar to it) to define
// a single encoding type that can be used in all parameters and
// data members that require one or the other or both.
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | CRYPT_ASN_ENCODING)
//-------------------------------------------------------------
// Get a pointer to the message content- this code creates
// the message content and gets a pointer to it. In reality,
// the content will usually exist somewhere and a pointer to it
// will get passed to the application.
//-------------------------------------------------------------
char szContent[] = "A Razzle-Dazzle \n"
"Enveloped Message"; // The message
char* pszContent = szContent; // A string pointer
BYTE* pbContent = (BYTE*) pszContent; // A byte pointer
DWORD cbContent = sizeof(szContent); // Size of message
AfxMessageBox(pszContent); // Display the messge
//-------------------------------------------------------------
// Get a handle to a crytographic provider.
//-------------------------------------------------------------
HCRYPTPROV hCryptProv = NULL; // Handle returned here
BOOL fReturn = FALSE;
fReturn = CryptAcquireContext(
&hCryptProv, // Address for handle to be returned
NULL, // Use the current user's logon name
NULL, // Use the default provider
PROV_RSA_FULL, // Specifies the provider type
0); // Zero allows access to private keys
if(!fReturn)
{
AfxMessageBox("CryptAcquireContext failed");
return;
}
// If the function succeeded, the handle to the cryptographic
// provider resides at hCryptProv.
//-------------------------------------------------------------
// Open the system certificate store.
//-------------------------------------------------------------
HCERTSTORE hStoreHandle = NULL;
// Call CertOpenSystemStore to open the store.
hStoreHandle = CertOpenSystemStore(hCryptProv, "MY");
if (!hStoreHandle)
{
AfxMessageBox( "Error Getting Store Handle");
return;
}
// If the call was successful, the cert store handle now resides
// at the location pointed to by hStoreHandle.
//-------------------------------------------------------------
// Get a pointer to the recipient certificate.
//-------------------------------------------------------------
PCCERT_CONTEXT pRecipCert = NULL;
// For the implementation of GetRecipientCert(), see the function
// at the end of this example code.
pRecipCert = GetRecipientCert(hStoreHandle, "James Allard");
if(!pRecipCert)
{
AfxMessageBox( "Error Getting Recipient Cert");
return;
}
// Create an array of CERT_INFOs. In this example, a single
// recipient.
PCERT_INFO RecipCertArray[1];
RecipCertArray[0] = pRecipCert->pCertInfo;
//-------------------------------------------------------------
// Initialize the symmetric encryption Algorithm Identifier
// structure.
//-------------------------------------------------------------
DWORD ContentEncryptAlgSize;
CRYPT_ALGORITHM_IDENTIFIER ContentEncryptAlgorithm;
ContentEncryptAlgSize = sizeof(ContentEncryptAlgorithm);
memset(&ContentEncryptAlgorithm, 0,
ContentEncryptAlgSize); // Init. to zero.
// Set the necessary fields. This particular OID doesn't
// need any parameters. Some OIDs, however will need the
// parameters member to be initialized.
ContentEncryptAlgorithm.pszObjId = szOID_RSA_RC4;
//-------------------------------------------------------------
// Initialize the CMSG_ENVELOPED_ENCODE_INFO structure.
//-------------------------------------------------------------
CMSG_ENVELOPED_ENCODE_INFO EnvelopedEncodeInfo;
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.
//-------------------------------------------------------------
DWORD cbEncodedBlob;
BYTE *pbEncodedBlob = NULL;
cbEncodedBlob = CryptMsgCalculateEncodedLength(
MY_ENCODING_TYPE, // Msg encoding type
0, // Flags
CMSG_ENVELOPED, // Msg type
&EnvelopedEncodeInfo, // Ptr. to struct.
NULL, // Inner content ObjID
cbContent); // size of content
if(0 == cbEncodedBlob)
{
AfxMessageBox("Getting cbEncodedBlob length failed");
return;
}
//-------------------------------------------------------------
// Allocate memory for the encoded blob.
//-------------------------------------------------------------
pbEncodedBlob = (BYTE *) malloc(cbEncodedBlob);
if(NULL == pbEncodedBlob)
{
AfxMessageBox("Malloc failed");
return;
}
//-------------------------------------------------------------
// Open a message to encode.
//-------------------------------------------------------------
HCRYPTMSG hMsg = NULL;
hMsg = CryptMsgOpenToEncode(
MY_ENCODING_TYPE, // Encoding type
0, // Flags
CMSG_ENVELOPED, // Msg type
&EnvelopedEncodeInfo, // Pointer to structure
NULL, // Inner content ObjID.
NULL); // Stream Info (not used)
if(NULL == hMsg)
{
AfxMessageBox("OpenToEncode failed");
free(pbEncodedBlob);
return;
}
//-------------------------------------------------------------
// Update the message with the data.
//-------------------------------------------------------------
BOOL fResult;
fResult = CryptMsgUpdate(
hMsg, // Handle to the message
pbContent, // Pointer to the content
cbContent, // Size of the content
TRUE); // Last call
if(!fResult)
{
AfxMessageBox("MsgUpdate failed");
free(pbEncodedBlob);
return;
}
//-------------------------------------------------------------
// Get the resultant message.
//-------------------------------------------------------------
fResult = CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_CONTENT_PARAM, // Parameter type
0, // Index
pbEncodedBlob, // Pointer to the blob
&cbEncodedBlob); // Size of the blob
if(!fResult)
{
AfxMessageBox("MsgGetParam failed");
free(pbEncodedBlob);
return;
}else
AfxMessageBox("Message encoded sucessfully");
// If the call was successful, pbEncodedBlob now points to the
// encoded, enveloped content.
BOOL fForceFlag = 0;
BOOL fAllCertsClosed = 1;
// If CERT_CLOSE_STORE_FORCE_FLAG is used with CertCloseStore,
// all open certificate contexts are closed and freed, and
// the store is closed. In some cases, such as multi-
// threaded programs, this may not be desirable. If
// CERT_CLOSE_STORE_CHECK_FLAG is used instead, the store will
// close but will warn you if all of the opened or duplicated
// certificate contexts have not been closed. Use
// CertFreeCertificateContext to close them when appropriate (as
// demonstrated in the "else" section of the following code).
if(fForceFlag)
{
CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
CryptMsgClose(hMsg);
}
else
{
CertFreeCertificateContext(pRecipCert);
fAllCertsClosed = CertCloseStore(hStoreHandle,
CERT_CLOSE_STORE_CHECK_FLAG);
if(!fAllCertsClosed)
AfxMessageBox("Certs are still open");
CryptMsgClose(hMsg);
}
//=============================================================
// EXAMPLE CODE FOR DECODING AN ENVELOPED MESSAGE
//=============================================================
//-------------------------------------------------------------
// Open a message for decoding.
//-------------------------------------------------------------
hMsg = CryptMsgOpenToDecode(
MY_ENCODING_TYPE, // Encoding type
0, // Flags
0, // Message type (get from msg.)
hCryptProv, // Cryptographic provider
NULL, // Recipient info
NULL); // Stream info
if(NULL == hMsg)
{
AfxMessageBox("OpenToDecode failed");
free(pbEncodedBlob);
return;
}
//-------------------------------------------------------------
// Update the message with the encoded blob, using the data
// that was encoded in the previous section of code.
//-------------------------------------------------------------
fResult = CryptMsgUpdate(
hMsg, // Handle to the message
pbEncodedBlob, // Pointer to the encoded blob
cbEncodedBlob, // Size of the encoded blob
TRUE); // Last call
if(!fResult)
{
AfxMessageBox("Decode MsgUpdate failed");
free(pbEncodedBlob);
return;
}
//-------------------------------------------------------------
// Get the message type.
//-------------------------------------------------------------
DWORD cbData = sizeof(DWORD);
DWORD dwMsgType = 0;
fResult = CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_TYPE_PARAM, // Parameter type
0, // Index
&dwMsgType, // Address for returned info
&cbData); // Size of the returned info
if(!fResult)
{
AfxMessageBox("Decode CMSG_TYPE_PARAM failed");
free(pbEncodedBlob);
return;
}
// Some applications may need to use a "switch" statement here
// and process the message differently, depending on the
// message type.
if(dwMsgType != CMSG_ENVELOPED)
{
AfxMessageBox("Wrong message type");
free(pbEncodedBlob);
return;
}
//-------------------------------------------------------------
// Get the size of inner content type BYTE string. If there is
// prior knowledge of inner data type, the next two function
// calls and the tests for the inner content type can be
// eliminated, and the decoding for that type of inner content
// can be implemented directly.
//-------------------------------------------------------------
DWORD cbInnerContentObjId = sizeof(DWORD);
fResult = CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_INNER_CONTENT_TYPE_PARAM, // Parameter type
0, // Index
NULL, // Address for returned info
&cbInnerContentObjId); // Size of the returned info
if(!fResult)
{
AfxMessageBox("Decode CMSG_INNER_CONTENT_TYPE_PARAM failed");
free(pbEncodedBlob);
return;
}
//-------------------------------------------------------------
// Allocate memory for the string.
//-------------------------------------------------------------
PBYTE pbInnerContentObjId = NULL;
pbInnerContentObjId = (PBYTE) malloc(cbInnerContentObjId);
if(NULL == pbInnerContentObjId)
{
AfxMessageBox("Decode inner content malloc failed");
free(pbEncodedBlob);
return;
}
//-------------------------------------------------------------
// Get the inner content type.
//-------------------------------------------------------------
fResult = CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_INNER_CONTENT_TYPE_PARAM, // Parameter type
0, // Index
pbInnerContentObjId, // Address for returned info
&cbInnerContentObjId); // Size of the returned info
if(!fResult)
{
AfxMessageBox("Decode CMSG_INNER_CONTENT_TYPE_PARAM #2 failed");
free(pbEncodedBlob);
free(pbInnerContentObjId);
return;
}
//-------------------------------------------------------------
// Process according to the inner content type.
//-------------------------------------------------------------
LPSTR pszInnerContentObjId = (LPSTR) pbInnerContentObjId;
// Test for the "data" data type.
if(!strcmp(pszInnerContentObjId, szOID_RSA_data))
{
//-----------------------------------------------------------
// Initialize the CMSG_CONTROL_DECRYPT_PARA structure.
//-----------------------------------------------------------
CMSG_CTRL_DECRYPT_PARA DecryptPara;
memset(&DecryptPara, 0, sizeof(CMSG_CTRL_DECRYPT_PARA));
DecryptPara.cbSize = sizeof(CMSG_CTRL_DECRYPT_PARA);
DecryptPara.hCryptProv = hCryptProv; // Using handle opened in
// "encode" code sect.
DecryptPara.dwKeySpec = AT_KEYEXCHANGE;
DecryptPara.dwRecipientIndex = 0;
//-----------------------------------------------------------
// Decrypt the enveloped message.
//-----------------------------------------------------------
fResult = CryptMsgControl(
hMsg, // Message handle
0, // Flags
CMSG_CTRL_DECRYPT, // Control type
&DecryptPara); // Address of the parameters
if(!fResult)
{
AfxMessageBox("Decode decryption failed");
free(pbEncodedBlob);
return;
}
//-----------------------------------------------------------
// Get the size of the content.
//-----------------------------------------------------------
DWORD cbDecoded = 0;
fResult = CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_CONTENT_PARAM, // Parameter type
0, // Index
NULL, // Address for returned info
&cbDecoded); // Size of the returned info
if(!fResult)
{
AfxMessageBox("Decode CMSG_CONTENT_PARAM failed");
free(pbEncodedBlob);
return;
}
//-----------------------------------------------------------
// Allocate Memory.
//-----------------------------------------------------------
BYTE *pbDecoded = NULL;
pbDecoded = (BYTE *) malloc(cbDecoded);
if(NULL == pbDecoded)
{
AfxMessageBox("Decode memory allocation failed");
free(pbEncodedBlob);
return;
}
//-----------------------------------------------------------
// Get a pointer to the content.
//-----------------------------------------------------------
LPSTR pDecodedString;
fResult = CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_CONTENT_PARAM, // Parameter type
0, // Index
pbDecoded, // Address for returned info
&cbDecoded); // Size of the returned info
if(!fResult)
{
AfxMessageBox("Decode CMSG_CONTENT_PARAM #2 failed");
free(pbEncodedBlob);
free(pbDecoded);
return;
}else
AfxMessageBox("Message decoded successfully");
pDecodedString = (LPSTR) pbDecoded;
AfxMessageBox(pDecodedString);
free(pbDecoded);
}
// Test for the "signed" data type.
else if(!strcmp(pszInnerContentObjId, szOID_RSA_signedData))
{
// Decode the "signed" data type here.
AfxMessageBox("Signed data type");
free(pbEncodedBlob);
free(pbInnerContentObjId);
return;
}
// Test for the "enveloped" data type.
else if(!strcmp(pszInnerContentObjId, szOID_RSA_envelopedData))
{
// Decode the "enveloped" data type here.
AfxMessageBox("Enveloped data type");
free(pbEncodedBlob);
free(pbInnerContentObjId);
return;
}
// Test for the "hashed" data type.
else if(!strcmp(pszInnerContentObjId, szOID_RSA_digestedData))
{
// Decode the "hashed" data type here.
AfxMessageBox("Hashed data type");
free(pbEncodedBlob);
free(pbInnerContentObjId);
return;
}
//-------------------------------------------------------------
// Clean up.
//-------------------------------------------------------------
free(pbEncodedBlob);
free(pbInnerContentObjId);
CryptMsgClose(hMsg);
}
//************************************************************
// GetRecipientCert() - A function to return the first
// certificate in the store that contains the subject name
// string passed in pszRecipName.
//************************************************************
PCCERT_CONTEXT GetRecipientCert(HCERTSTORE hCertStore,
LPSTR pszRecipName)
{
PCCERT_CONTEXT pPrevCertContext = NULL;
PCCERT_CONTEXT pCertContext = NULL;
BOOL fTestFlag;
// This example code will find the first occurrence of a
// certificate containing a subject name passed in to
// pszRecipName. That certificate must contain an exchange public
// key (not a signature public key). If you suspect that more
// than one certificate exists for the desired individual, then
// additional checking must be done to determine that the returned
// certificate contains an exchange public key.
while(TRUE)
{
pCertContext = CertFindCertificateInStore(
hCertStore,
X509_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR_A,
pszRecipName,
pPrevCertContext);
if(!pCertContext)
{
fTestFlag = FALSE; // No match found
break;
}
else
{
fTestFlag = TRUE; // Match found.
break;
}
pPrevCertContext = pCertContext;
}
CertFreeCertificateContext(pPrevCertContext);
if(!fTestFlag)
{
AfxMessageBox("Recipient Cert Not Found");
return NULL;
}else
return (pCertContext);
}