Example C Program: Encoding and Decoding a Countersigned Message
The following example shows how to encode and decode a countersigned message. This example uses the MyHandleError example function. Code for the MyHandleError function and other auxiliary functions is also listed under General Purpose Functions.
#include <stdafx.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <wincrypt.h>
// Link with the Crypt32.lib file.
#pragma comment (lib, "crypt32")
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
//-------------------------------------------------------------------
// Define the names of two certificate subjects.
// To use this program, the definitions of SIGNER_NAME and
// COUNTER_SIGNER_NAME must be changed to the names of
// the subjects of certificates that have access to private keys.
// These certificates must have either the
// CERT_KEY_PROV_INFO_PROP_ID or CERT_KEY_CONTEXT_PROP_ID
// property set for the contexts to provide access to private
// signature keys.
#define SIGNER_NAME L"Insert_signer_name_here"
#define COUNTER_SIGNER_NAME L"Insert_counter_signer_name_here"
#define MAX_NAME 256
void MyHandleError(char *s);
int _tmain(int argc, _TCHAR* argv[])
{
//---------------------------------------------------------------
// 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;
DWORD cbContent;
HCRYPTPROV hCryptProv;
HCERTSTORE hStoreHandle;
PCCERT_CONTEXT pSignerCert;
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;
char pszNameString[MAX_NAME];
CRYPT_VERIFY_MESSAGE_PARA VerifyParams;
BYTE *pbDecodedMessageBlob;
DWORD cbDecodedMessageBlob;
DWORD dwKeySpec;
// The message.
pbContent = (BYTE*)"I must go back to where all messages start.";
//---------------------------------------------------------------
// Begin processing.
//---------------------------------------------------------------
// Initialize cbContent to the length of pbContent
// including the terminating NULL character.
cbContent = lstrlenA((char *) pbContent) + 1;
printf("The example message is ->.\n");
printf("%s\n\n", pbContent);
//---------------------------------------------------------------
// Open the MY system certificate store.
if(!(hStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
L"MY")))
{
MyHandleError("Could not open the MY system store.");
}
//---------------------------------------------------------------
// Get a pointer to a signer's signature certificate.
if(pSignerCert = CertFindCertificateInStore(
hStoreHandle,
MY_ENCODING_TYPE,
0,
CERT_FIND_SUBJECT_STR,
SIGNER_NAME,
NULL))
{
//-----------------------------------------------------------
// A certificate was found. Get and print the name of the
// subject of the certificate.
if(CertGetNameStringA(
pSignerCert,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
pszNameString,
MAX_NAME) > 1)
{
printf("The message signer is %s.\n",pszNameString);
}
else
{
MyHandleError("Getting the signer name failed.\n");
}
}
else
{
MyHandleError("Cert not found.\n");
}
//---------------------------------------------------------------
// Initialize the CMSG_SIGNER_ENCODE_INFO structure.
//---------------------------------------------------------------
// Get a handle to a cryptographic provider.
if(!(CryptAcquireCertificatePrivateKey(
pSignerCert,
0,
NULL,
&hCryptProv,
&dwKeySpec,
NULL)))
{
MyHandleError("CryptAcquireContext failed.");
}
memset(&SignerEncodeInfo, 0, sizeof(CMSG_SIGNER_ENCODE_INFO));
SignerEncodeInfo.cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
SignerEncodeInfo.pCertInfo = pSignerCert->pCertInfo;
SignerEncodeInfo.hCryptProv = hCryptProv;
SignerEncodeInfo.dwKeySpec = dwKeySpec;
SignerEncodeInfo.HashAlgorithm.pszObjId = szOID_RSA_MD5;
SignerEncodeInfo.pvHashAuxInfo = NULL;
//---------------------------------------------------------------
// Initialize the first element of an array of signers.
// Note: Currently, there is 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.
// Note: In this program, only one signer BLOB is 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;
//---------------------------------------------------------------
// Get the size of the encoded message BLOB.
cbEncodedBlob = CryptMsgCalculateEncodedLength(
MY_ENCODING_TYPE,
0,
CMSG_SIGNED,
&SignedMsgEncodeInfo,
NULL,
cbContent);
if(!cbEncodedBlob)
{
MyHandleError("Getting cbEncodedBlob length failed.");
}
//---------------------------------------------------------------
// Allocate memory for the encoded BLOB.
pbEncodedBlob = (BYTE *) malloc(cbEncodedBlob);
if(!pbEncodedBlob)
{
MyHandleError("malloc operation failed.");
}
//---------------------------------------------------------------
// Open a message to encode.
hMsg = CryptMsgOpenToEncode(
MY_ENCODING_TYPE,
0,
CMSG_SIGNED,
&SignedMsgEncodeInfo,
NULL,
NULL);
if(!hMsg)
{
MyHandleError("OpenToEncode failed.");
}
//---------------------------------------------------------------
// Update the message with the data.
if(!(CryptMsgUpdate(
hMsg,
pbContent,
cbContent,
TRUE)))
{
MyHandleError("CryptMsgUpdate failed.");
}
//---------------------------------------------------------------
// Get the resulting message.
if(CryptMsgGetParam(
hMsg,
CMSG_CONTENT_PARAM,
0,
pbEncodedBlob,
&cbEncodedBlob))
{
printf("Message successfully signed.\n");
}
else
{
MyHandleError("CryptMsgGetParam failed.");
}
//---------------------------------------------------------------
// The message is signed and encoded.
// Close the message handle and the certificate store.
CryptMsgClose(hMsg);
CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
CryptReleaseContext(hCryptProv,0);
//---------------------------------------------------------------
// Next, countersign the signed message.
// Assume that pbEncodedBlob, the message just created, was sent
// to an intended recipient.
// From the recipient's point of view, the following code
// completes these steps:
// 1. Decodes the message
// 2. Verifies the signature on the message
// 3. Adds a countersignature to the signed message
//
// The counter-signed message is returned to the original signer
// of the message, where the counter-signature is verified.
//---------------------------------------------------------------
// Open a message for decoding.
hMsg = CryptMsgOpenToDecode(
MY_ENCODING_TYPE,
0,
0,
NULL,
NULL,
NULL);
if(!hMsg)
{
MyHandleError("CryptOpenToDecode failed.");
}
//---------------------------------------------------------------
// Update the message with the encoded BLOB.
if(!(CryptMsgUpdate(
hMsg,
pbEncodedBlob,
cbEncodedBlob,
TRUE)))
{
MyHandleError("Decode CryptMsgUpdate failed.");
}
//---------------------------------------------------------------
// Get the size of the message.
if(CryptMsgGetParam(
hMsg,
CMSG_CONTENT_PARAM,
0,
NULL,
&cbDecoded))
{
printf("The message is %d bytes long.\n", cbDecoded);
}
else
{
MyHandleError("Decode CMSG_CONTENT_PARAM failed.");
}
//---------------------------------------------------------------
// Allocate memory.
pbDecoded = (BYTE*)malloc(cbDecoded);
if(!pbDecoded)
{
MyHandleError("Decode memory allocation failed.");
}
//---------------------------------------------------------------
// Copy the message to the buffer.
if(CryptMsgGetParam(
hMsg,
CMSG_CONTENT_PARAM,
0,
pbDecoded,
&cbDecoded))
{
printf("The successfully decoded message is -> ");
printf("%s\n", pbDecoded);
}
else
{
MyHandleError("Decode CMSG_CONTENT_PARAM #2 failed.");
}
//---------------------------------------------------------------
// Check the signature.
// Initialize the VerifyParams data structure.
VerifyParams.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
VerifyParams.dwMsgAndCertEncodingType = MY_ENCODING_TYPE;
VerifyParams.hCryptProv = 0;
VerifyParams.pfnGetSignerCertificate = NULL;
VerifyParams.pvGetArg = NULL;
if(!(CryptVerifyMessageSignature(
&VerifyParams,
0,
pbEncodedBlob,
cbEncodedBlob,
NULL,
&cbDecodedMessageBlob,
NULL)))
{
printf("Getting the size of the verification message " \
"failed.\n");
}
pbDecodedMessageBlob = (BYTE*)malloc(cbDecodedMessageBlob);
if(!pbDecodedMessageBlob)
{
MyHandleError("Memory allocation failed.");
}
if(CryptVerifyMessageSignature(
&VerifyParams,
0,
pbEncodedBlob,
cbEncodedBlob,
pbDecodedMessageBlob,
&cbDecodedMessageBlob,
NULL))
{
printf("The Signature verified message is -> \n");
printf("%s \n\n",pbDecodedMessageBlob);
}
else
{
MyHandleError("Verification message failed.");
}
//---------------------------------------------------------------
// Proceed with the countersigning.
// First, open a certificate store.
if(!(hStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
L"MY")))
{
MyHandleError("Could not open the MY system store.");
}
//---------------------------------------------------------------
// Get the countersigner's certificate.
if(pCntrSigCert = CertFindCertificateInStore(
hStoreHandle,
MY_ENCODING_TYPE,
0,
CERT_FIND_SUBJECT_STR,
COUNTER_SIGNER_NAME,
NULL))
{
if(CertGetNameStringA(
pCntrSigCert ,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
pszNameString,
MAX_NAME) > 1)
{
printf("The counter signer is %s.\n", pszNameString);
}
else
{
MyHandleError("Getting the countersigner name " \
"failed.\n");
}
}
else
{
MyHandleError("Could not find the countersigner's " \
"certificate.");
}
//---------------------------------------------------------------
// Initialize the CMSG_SIGNER_ENCODE_INFO structure.
if(!(CryptAcquireCertificatePrivateKey(
pCntrSigCert,
0,
NULL,
&hCryptProv,
&dwKeySpec,
NULL)))
{
MyHandleError("CryptAcquireContext failed.");
}
memset(&CountersignerInfo, 0, sizeof(CMSG_SIGNER_ENCODE_INFO));
CountersignerInfo.cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
CountersignerInfo.pCertInfo = pCntrSigCert->pCertInfo;
CountersignerInfo.hCryptProv = hCryptProv;
CountersignerInfo.dwKeySpec = dwKeySpec;
CountersignerInfo.HashAlgorithm.pszObjId = szOID_RSA_MD5;
CntrSignArray[0] = CountersignerInfo;
//---------------------------------------------------------------
// Countersign the message.
if(CryptMsgCountersign(
hMsg,
0,
1,
CntrSignArray))
{
printf("CryptMsgCountersign succeeded.\n");
}
else
{
MyHandleError("CryptMsgCountersign failed.");
}
//---------------------------------------------------------------
// Get a pointer to the new, countersigned message BLOB.
// Get the size of memory required.
if(CryptMsgGetParam(
hMsg,
CMSG_ENCODED_MESSAGE,
0,
NULL,
&cbEncodedBlob))
{
printf("The size of the encoded BLOB is %d.\n",
cbEncodedBlob);
}
else
{
MyHandleError("Sizing of cbSignerInfo failed.");
}
//---------------------------------------------------------------
// Allocate memory.
pbEncodedBlob = (BYTE*) malloc(cbEncodedBlob);
if(pbEncodedBlob)
{
printf("%d bytes allocated.\n", cbEncodedBlob);
}
else
{
MyHandleError("cbSignerInfo memory allocation failed.");
}
//---------------------------------------------------------------
// Get the new message encoded BLOB.
if(CryptMsgGetParam(
hMsg,
CMSG_ENCODED_MESSAGE,
0,
pbEncodedBlob,
&cbEncodedBlob))
{
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,
0,
0,
0,
NULL,
NULL))
{
printf("The message to decode has been opened.\n");
}
else
{
MyHandleError("CryptMsgOpenToDecode failed.");
}
//---------------------------------------------------------------
// Update the message with the encoded BLOB.
if(CryptMsgUpdate(
hMsg,
pbEncodedBlob,
cbEncodedBlob,
TRUE))
{
printf("The message to decode has been updated.\n");
}
else
{
MyHandleError("Updating the countersignature message " \
"failed.");
}
//---------------------------------------------------------------
// Get a pointer to the CERT_INFO member of
// countersigner's certificate.
//---------------------------------------------------------------
// Retrieve the signer information from the message.
// Get the size of memory required.
if(CryptMsgGetParam(
hMsg,
CMSG_ENCODED_SIGNER,
0,
NULL,
&cbSignerInfo))
{
printf("Signer information is %d bytes.\n", cbSignerInfo);
}
else
{
MyHandleError("Sizing of cbSignerInfo failed.");
}
//---------------------------------------------------------------
// Allocate memory.
pbSignerInfo = (BYTE*) malloc(cbSignerInfo);
if(!pbSignerInfo)
{
MyHandleError("cbSignerInfo memory allocation failed.");
}
//---------------------------------------------------------------
// Get the message signer information.
if(!(CryptMsgGetParam(
hMsg,
CMSG_ENCODED_SIGNER,
0,
pbSignerInfo,
&cbSignerInfo)))
{
MyHandleError("Getting pbSignerInfo failed.");
}
//---------------------------------------------------------------
// Retrieve the countersigner information from the message.
// Get the size of memory required.
if(CryptMsgGetParam(
hMsg,
CMSG_SIGNER_UNAUTH_ATTR_PARAM,
0,
NULL,
&cbCountersignerInfo))
{
printf("Counter Signer information is %d bytes.\n",
cbCountersignerInfo);
}
else
{
MyHandleError("Sizing of cbCountersignerInfo failed.");
}
//---------------------------------------------------------------
// Allocate memory.
pCountersignerInfo =
(CRYPT_ATTRIBUTES*)malloc(cbCountersignerInfo);
if(!pCountersignerInfo)
{
MyHandleError("pbCountersignInfo memory allocation failed.");
}
//---------------------------------------------------------------
// Get the message counter signer info.
if(!(CryptMsgGetParam(
hMsg,
CMSG_SIGNER_UNAUTH_ATTR_PARAM,
0,
pCountersignerInfo,
&cbCountersignerInfo)))
{
MyHandleError("Getting pbCountersignerInfo failed.");
}
//---------------------------------------------------------------
// Verify the countersignature.
if(CryptMsgVerifyCountersignatureEncoded(
0,
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");
if(GetLastError() == NTE_BAD_SIGNATURE)
{
printf("Bad signature.\n");
}
else
{
printf("Other verification error.\n");
}
}
//---------------------------------------------------------------
// Clean up.
free(pbEncodedBlob);
free(pbDecoded);
free(pbSignerInfo);
free(pCountersignerInfo);
CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
CryptMsgClose(hMsg);
CryptReleaseContext(hCryptProv,0);
return 0;
}
//-------------------------------------------------------------------
// 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);
}