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);
}