Alternate Code for Encoding an Enveloped Message
The following example demonstrates an alternate process of encoding a signed message, using that signed message as the inner content for an enveloped message. In preparation for decoding, the inner content is tested to determine its inner-content type.
This example illustrates the following CryptoAPI functions:
- CryptAcquireContext
- CertOpenSystemStore
- CryptMsgCalculateEncodedLength
- CryptMsgOpenToEncode
- CryptMsgUpdate
- CryptMsgGetParam
- CryptMsgOpenToDecode
- CertFindCertificateInStore
- CryptMsgClose
- CertCloseStore
- CryptReleaseContext
This example also uses the functions MyHandleError and GetSignerCert. C code for these functions is included with the example. For code that demonstrates these and other auxiliary functions, see General Purpose Functions.
//--------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// In this and all other examples,
// use the #define and
// #include statements listed under #includes and #defines.
#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);
//--------------------------------------------------------------------
// This program uses the function GetSignerCert, declared here and
// defined after main.
PCCERT_CONTEXT GetSignerCert(
HCERTSTORE hCertStore);
void main(void)
{
//--------------------------------------------------------------------
// Declare and initialize variables. This includes declaring and
// initializing a pointer to message content to be countersigned
// and encoded. Usually, the message content will exist somewhere,
// and a pointer to it is passed to the application.
BYTE* pbContent = (BYTE*)"The message to be countersigned.";
// The message
DWORD cbContent; // Size of message
HCRYPTPROV hCryptProv; // CSP handle
HCERTSTORE hStoreHandle; // Store handle
PCCERT_CONTEXT pSignerCert; // Signer certificate
DWORD HashAlgSize;
CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo;
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfoArray[1];
CERT_BLOB SignerCertBlob;
CERT_BLOB SignerCertBlobArray[1];
CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo;
DWORD cbEncodedBlob;
BYTE* pbEncodedBlob;
HCRYPTMSG hMsg;
DWORD cbDecoded;
BYTE *pbDecoded;
PCCERT_CONTEXT pCntrSigCert;
CMSG_SIGNER_ENCODE_INFO CountersignerInfo;
CMSG_SIGNER_ENCODE_INFO CntrSignArray[1];
DWORD cbSignerInfo;
PBYTE pbSignerInfo;
DWORD cbCountersignerInfo;
PCRYPT_ATTRIBUTES pCountersignerInfo;
//--------------------------------------------------------------------
// Begin processing.
cbContent = strlen((char *) pbContent)+1;
// One is added to include the final NULL character.
printf("Processing begins.\n");
printf("The length of the original message is %d.\n",cbContent);
printf("Example message:->%s\n", pbContent);
//--------------------------------------------------------------------
// Get a handle to a cryptographic provider.
if(CryptAcquireContext(
&hCryptProv, // Address for handle to be returned
NULL, // Use the logon name for the current user
NULL, // Use the default provider
PROV_RSA_FULL, // Provider type
0)) // Zero allows access to private keys
{
printf("The CSP has been opened.");
}
else
{
MyHandleError("CryptAcquireContext failed");
}
//--------------------------------------------------------------------
// Open the MY system certificate store.
if(hStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM, // The system store will be a
// virtual store.
0, // Encoding type not needed with
// this PROV.
NULL, // Accept the default HCRYPTPROV.
CERT_SYSTEM_STORE_CURRENT_USER,
// Set the system store location in the
// registry.
L"MY")) // Other predefined system stores
// could have been used,
// including trust, CA, or root.
{
printf("Opened the MY system store. \n");
}else
{
MyHandleError( "Could not open the MY system store.");
}
//--------------------------------------------------------------------
// Get a pointer to the signature certificate of the signer.
if(pSignerCert = GetSignerCert(hStoreHandle))
{
printf("A signer certificate was found. \n");
}
else
{
MyHandleError("Error getting signer certificate.");
}
//--------------------------------------------------------------------
// Initialize the algorithm identifier structure.
HashAlgSize = sizeof(HashAlgorithm);
memset(&HashAlgorithm, 0, HashAlgSize); // Initialize to zero,
HashAlgorithm.pszObjId = szOID_RSA_MD5; // then set 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 = hCryptProv;
SignerEncodeInfo.dwKeySpec = AT_KEYEXCHANGE;
SignerEncodeInfo.HashAlgorithm = HashAlgorithm;
SignerEncodeInfo.pvHashAuxInfo = NULL;
//--------------------------------------------------------------------
// Initialize the first element of an array of signers.
// There can be only one signer.
SignerEncodeInfoArray[0] = SignerEncodeInfo;
//--------------------------------------------------------------------
// Initialize the CMSG_SIGNED_ENCODE_INFO structure.
SignerCertBlob.cbData = pSignerCert->cbCertEncoded;
SignerCertBlob.pbData = pSignerCert->pbCertEncoded;
//--------------------------------------------------------------------
// Initialize the first element of an array of signer BLOBs.
// Only one signer BLOB used.
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 message BLOB.
if(cbEncodedBlob = 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("The size for the encoded BLOB is %d.\n",cbEncodedBlob);
}
else
{
MyHandleError("Getting 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("Malloc operation 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 encode is open. \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("Message to encode has been updated. \n");
}
else
{
MyHandleError("MsgUpdate failed");
}
//--------------------------------------------------------------------
// Get the resulting message.
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_CONTENT_PARAM, // Parameter type
0, // Index
pbEncodedBlob, // Pointer to the BLOB
&cbEncodedBlob)) // Size of the BLOB
{
printf("Message successfully signed. \n");
}
else
{
MyHandleError("MsgGetParam failed.");
}
//--------------------------------------------------------------------
// pbEncodedBlob points to the encoded, signed content.
// Include any further processing here.
CryptMsgClose(hMsg); // The message is complete--close the handle.
//--------------------------------------------------------------------
// Next, countersign the signed message.
// Assume that the message just created and that a pointer
// (pbEncodedBlob) to the message were sent to the intended
// recipient.
// The following code, from the recipient's point of view, adds a
// countersignature to the signed message.
//
// Before countersigning, the message must be decoded.
//--------------------------------------------------------------------
// Open a message for decoding.
if(hMsg = CryptMsgOpenToDecode(
MY_ENCODING_TYPE, // Encoding type
0, // Flags
0, // Message type (get from message)
hCryptProv, // Cryptographic provider
NULL, // Recipient information
NULL)) // Stream information
{
printf("The message for decoding has been opened. \n");
}
else
{
MyHandleError("OpenToDecode failed.");
}
//--------------------------------------------------------------------
// Update the message with the data (encoded BLOB).
// In this example, pbEncodedBlob and cbEncodedBlob were
// created in the previous code.
if(CryptMsgUpdate(
hMsg, // Handle to the message
pbEncodedBlob, // Pointer to the encoded BLOB
cbEncodedBlob, // Size of the encoded BLOB
TRUE)) // Last call
{
printf("The message to be decoded has been updated. \n");
}
else
{
MyHandleError("Decode MsgUpdate failed.");
}
//--------------------------------------------------------------------
// Get the size of the content.
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_CONTENT_PARAM, // Parameter type
0, // Index
NULL, // Address for returned
// information
&cbDecoded)) // Size of the returned
// information
{
printf("The message to be decoded is %d bytes long. \n",
cbDecoded);
}
else
{
MyHandleError("Decode CMSG_CONTENT_PARAM failed");
}
//--------------------------------------------------------------------
// Allocate memory.
if(pbDecoded = (BYTE *) malloc(cbDecoded))
{
printf("Memory has been allocated. \n");
}
else
{
MyHandleError("Decode memory allocation failed");
}
//--------------------------------------------------------------------
// Get a pointer to the content.
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_CONTENT_PARAM, // Parameter type
0, // Index
pbDecoded, // Address for returned information
&cbDecoded)) // Size of the returned information
{
printf("The successfully decoded message is =>%s\n",pbDecoded);
}
else
{
MyHandleError("Decode CMSG_CONTENT_PARAM #2 failed.");
}
//--------------------------------------------------------------------
// Proceed with the countersigning.
//--------------------------------------------------------------------
// Initialize the CRYPT_ALGORITHM_IDENTIFIER structure. In this
// case, the initialization performed for signing the message in
// the previous code is used.
//--------------------------------------------------------------------
// Get the certificate of the countersigner. A certificate with a
// subject name that matches the string in parameter five must
// be in the MY store and must have its CERT_KEY_PROV_INFO_PROP_ID
// property set.
if(pCntrSigCert=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.
L"Full Test Cert", // 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("The desired certificate was found. \n");
}
else
{
MyHandleError("Could not find the countersigner's certificate.");
}
//--------------------------------------------------------------------
// Initialize the PCMSG_SIGNER_ENCODE_INFO structure.
memset(&CountersignerInfo, 0, sizeof(CMSG_SIGNER_ENCODE_INFO));
CountersignerInfo.cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
CountersignerInfo.pCertInfo = pCntrSigCert->pCertInfo;
CountersignerInfo.hCryptProv = hCryptProv;
CountersignerInfo.dwKeySpec = AT_KEYEXCHANGE;
CountersignerInfo.HashAlgorithm = HashAlgorithm;
CntrSignArray[0] = CountersignerInfo;
//--------------------------------------------------------------------
// Countersign the message.
if(CryptMsgCountersign(
hMsg,
0,
1,
CntrSignArray))
{
printf("Countersign succeeded. \n");
}
else
{
MyHandleError("Countersign failed.");
}
//--------------------------------------------------------------------
// Get a pointer to the new, countersigned message BLOB.
// Get the size of memory required.
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_ENCODED_MESSAGE, // Parameter type
0, // Index
NULL, // Address for returned information
&cbEncodedBlob)) // Size of the returned information
{
printf("The size for the encoded BLOB is %d.\n",cbEncodedBlob);
}
else
{
MyHandleError("Sizing of cbSignerInfo failed.");
}
//--------------------------------------------------------------------
// Allocate memory.
if(pbEncodedBlob = (BYTE*) malloc(cbEncodedBlob))
{
printf("%d bytes allocated .\n", cbEncodedBlob);
}
else
{
MyHandleError("cbSignerInfo memory allocation failed");
}
//--------------------------------------------------------------------
// Get the new message encoded BLOB.
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_ENCODED_MESSAGE, // Parameter type
0, // Index
pbEncodedBlob, // Address for returned information
&cbEncodedBlob)) // Size of the returned information
{
printf("The message is complete. \n");
}
else
{
MyHandleError("Getting pbEncodedBlob failed");
}
//-------------------------------------------------------------------
// The message is complete. Close the handle.
CryptMsgClose(hMsg);
//--------------------------------------------------------------------
// Verify the countersignature.
// Assume that the countersigned message went back to the originator,
// where, again, it will be decoded.
//--------------------------------------------------------------------
// Before verifying the countersignature, the message must first
// be decoded.
//--------------------------------------------------------------------
// Open a message for decoding.
if(hMsg = CryptMsgOpenToDecode(
MY_ENCODING_TYPE, // Encoding type
0, // Flags
0, // Message type (get from message)
hCryptProv, // Cryptographic provider
NULL, // Recipient information
NULL)) // Stream information
{
printf("The message to decode has been opened. \n");
}
else
{
MyHandleError("OpenToDecode failed.");
}
//--------------------------------------------------------------------
// Update the message with the encoded BLOB.
// In this example, pbEncodedBlob and cbEncodedBlob were
// initialized in the previous code.
if(CryptMsgUpdate(
hMsg, // Handle to the message
pbEncodedBlob, // Pointer to the encoded BLOB
cbEncodedBlob, // Size of the encoded BLOB
TRUE)) // Last call
{
printf("The message to decode has been updated. \n");
}
else
{
MyHandleError("Updating of the verified countersignature "
"message failed.");
}
//-------------------------------------------------------------
// Get a pointer to the CERT_INFO member of the certificate of the
// countersigner. In this case, the certificate retrieved in the
// previous code segment will be used (pCntrSigCert).
//--------------------------------------------------------------
// Retrieve the signer information from the message.
// Get the size of memory required.
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_ENCODED_SIGNER, // Parameter type
0, // Index
NULL, // Address for returned information
&cbSignerInfo)) // Size of the returned information
{
printf("The size of the signer information has been "
"retrieved.\n");
}
else
{
MyHandleError("Sizing of cbSignerInfo failed.");
}
//--------------------------------------------------------------------
// Allocate memory.
if(pbSignerInfo = (BYTE*) malloc(cbSignerInfo))
{
printf("%d bytes allocated for the signer "
"information. \n", cbSignerInfo);
}
else
{
MyHandleError("cbSignerInfo memory allocation failed");
}
//--------------------------------------------------------------------
// Get the message signer information.
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_ENCODED_SIGNER, // Parameter type
0, // Index
pbSignerInfo, // Address for returned information
&cbSignerInfo)) // Size of the returned information
{
printf("The signer information is retrieved. \n");
}
else
{
MyHandleError("Getting pbSignerInfo failed.");
}
//--------------------------------------------------------------------
// Retrieve the countersigner information from the message.
// Get the size of memory required.
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_SIGNER_UNAUTH_ATTR_PARAM,// Parameter type
0, // Index
NULL, // Address for returned
// information
&cbCountersignerInfo)) // Size of returned
// information
{
printf("The length of the countersigner's information is "
"retrieved. \n");
}
else
{
MyHandleError("Sizing of cbCountersignerInfo failed.");
}
//--------------------------------------------------------------------
// Allocate memory.
if(pCountersignerInfo =
(CRYPT_ATTRIBUTES*)malloc(cbCountersignerInfo))
{
printf("%d bytes allocated. \n", cbCountersignerInfo);
}
else
{
MyHandleError("pbCountersignInfo memory allocation failed.");
}
//--------------------------------------------------------------------
// Get the message SIGNER_INFO.
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_SIGNER_UNAUTH_ATTR_PARAM, // Parameter type
0, // Index
pCountersignerInfo, // Address for returned
// information
&cbCountersignerInfo)) // Size of the returned
// information
{
printf("Countersigner information retrieved. \n");
}
else
{
MyHandleError("Getting pbCountersignerInfo failed.");
}
//--------------------------------------------------------------------
// Verify the countersignature.
if(CryptMsgVerifyCountersignatureEncoded(
hCryptProv,
MY_ENCODING_TYPE,
pbSignerInfo,
cbSignerInfo,
pCountersignerInfo->rgAttr->rgValue->pbData,
pCountersignerInfo->rgAttr->rgValue->cbData,
pCntrSigCert->pCertInfo))
{
printf("Verification of countersignature succeeded. \n");
}
else
{
printf("Verification of countersignature failed. \n");
}
//--------------------------------------------------------------------
// Clean up.
free(pbEncodedBlob);
free(pbDecoded);
free(pbSignerInfo);
free(pCountersignerInfo);
CertCloseStore(
hStoreHandle,
CERT_CLOSE_STORE_FORCE_FLAG);
CryptMsgClose(hMsg);
CryptReleaseContext(hCryptProv,0);
} // End of main
//--------------------------------------------------------------------
// Define function MyHandleError.
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);
}
// GetSignerCert enumerates the certificates in a store and
// finds the first certificate that has a signature key. If a
// certificate is found, a pointer to the certificate is returned.
PCCERT_CONTEXT GetSignerCert(
HCERTSTORE hCertStore)
//--------------------------------------------------------------------
// Parameter passed in:
// hCertStore, the handle of the store to be searched.
{
//--------------------------------------------------------------------
// Declare and initialize local variables.
PCCERT_CONTEXT pCertContext = NULL;
BOOL fMore = TRUE;
DWORD dwSize = NULL;
CRYPT_KEY_PROV_INFO* pKeyInfo = NULL;
DWORD PropId = CERT_KEY_PROV_INFO_PROP_ID;
//--------------------------------------------------------------------
// Find certificates in the store until the end of the store
// is reached or a certificate with an AT_SIGNATURE key is found.
while(fMore &&
(pCertContext= CertFindCertificateInStore(
hCertStore, // Handle of the store to be searched.
0, // Encoding type. Not used for this search.
0, // dwFindFlags. Special find criteria.
// Not used in this search.
CERT_FIND_PROPERTY, // Find type. Determines the kind of
// search to be done. In this case, search
// for certificates that have a specific
// extended property.
&PropId, // pvFindPara. Gives the specific
// value searched for, here the identifier
// of an extended property.
pCertContext))) // pCertContext is NULL for the
// first call to the function.
// If the function were being called
// in a loop, after the first call,
// pCertContext would be the certificate
// returned by the previous call.
{
//-------------------------------------------------------------
// For simplicity, this code only searches
// for the first occurrence of an AT_SIGNATURE key.
// In many situations, a search would also look for a
// specific subject name as well as the key type.
//-------------------------------------------------------------
// Call CertGetCertificateContextProperty once to get the
// returned structure size.
if(!(CertGetCertificateContextProperty(
pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
NULL,
&dwSize)))
{
MyHandleError("Error getting key property");
}
//--------------------------------------------------------------
// Allocate memory for the returned structure.
if(pKeyInfo)
free(pKeyInfo);
if(!(pKeyInfo = (CRYPT_KEY_PROV_INFO*)malloc(dwSize)))
{
MyHandleError("Error allocating memory for pKeyInfo");
}
//--------------------------------------------------------------
// Get the key information structure.
if(!(CertGetCertificateContextProperty(
pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
pKeyInfo,
&dwSize)))
{
MyHandleError("The second call to the function failed.");
}
//-------------------------------------------
// Check the dwKeySpec member for a signature key.
if(pKeyInfo->dwKeySpec == AT_SIGNATURE)
{
fMore = FALSE;
}
} // End of while loop
if(pKeyInfo)
free(pKeyInfo);
return (pCertContext);
} // End of GetSignerCert.