Signed Message Example Code

The following example code demonstrates the process of encoding, decoding, and verifying the signature of a signed message.

//=============================================================
// EXAMPLE CODE FOR CREATING A SIGNED 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"
                   "Signed 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 original message

//-------------------------------------------------------------
// Get a handle to a cryptographic 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 your signature certificate.
//-------------------------------------------------------------
PCCERT_CONTEXT    pSignerCert = NULL;
// For the implementation of GetMySignerCert(), see the function
// at the end of this example code.
pSignerCert = GetMySignerCert(hStoreHandle);
if(!pSignerCert)
{
    AfxMessageBox( "Error Getting Signer Cert");
    return;
}

//-------------------------------------------------------------
// Initialize the Algorithm Identifier structure.
//-------------------------------------------------------------
DWORD                         HashAlgSize;
CRYPT_ALGORITHM_IDENTIFIER    HashAlgorithm;
HashAlgSize = sizeof(HashAlgorithm);
memset(&HashAlgorithm, 0, HashAlgSize);    // Init. to zero.
HashAlgorithm.pszObjId = szOID_RSA_MD5;    // Then set the 
                                           //   necessary field.

//-------------------------------------------------------------
// Initialize the CMSG_SIGNER_ENCODE_INFO structure.
//-------------------------------------------------------------
CMSG_SIGNER_ENCODE_INFO    SignerEncodeInfo;

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_SIGNATURE;
SignerEncodeInfo.HashAlgorithm = HashAlgorithm;
SignerEncodeInfo.pvHashAuxInfo = NULL;

// Create an array of one. NOTE: Currently, there can be only one
// signer.
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfoArray[1];
SignerEncodeInfoArray[0] = SignerEncodeInfo;

//-------------------------------------------------------------
// Initialize the CMSG_SIGNED_ENCODE_INFO structure.
//-------------------------------------------------------------
CERT_BLOB    SignerCertBlob;

SignerCertBlob.cbData = pSignerCert->cbCertEncoded;
SignerCertBlob.pbData = pSignerCert->pbCertEncoded;

// Create an array of one. Only one certificate is included.
CERT_BLOB    SignerCertBlobArray[1];
SignerCertBlobArray[0] = SignerCertBlob;

CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo;

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.
//-------------------------------------------------------------
DWORD        cbEncodedBlob;
BYTE        *pbEncodedBlob = NULL;

cbEncodedBlob = CryptMsgCalculateEncodedLength(
                        MY_ENCODING_TYPE,       // Msg encoding type
                        0,                      // Flags
                        CMSG_SIGNED,            // Msg type
                        &SignedMsgEncodeInfo,   // 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_SIGNED,             // Msg type
                &SignedMsgEncodeInfo,    // 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);
    CryptMsgClose(hMsg);
    return;
}else
    AfxMessageBox("Message encoded sucessfully");

// If the call was successful, pbEncodedBlob now points to the
// encoded, signed content.

CertFreeCertificateContext(pSignerCert);
CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
CryptMsgClose(hMsg);

//=============================================================
// EXAMPLE CODE FOR DECODING A SIGNED MESSGE
//=============================================================

//-------------------------------------------------------------
// 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_SIGNED)
{
    AfxMessageBox("Wrong message type");
    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);

//=============================================================
// Verify the signature.
//=============================================================

//-------------------------------------------------------------
// Get the signer CERT_INFO from the message.
//-------------------------------------------------------------

// Get the size of memory required.
DWORD    cbSignerCertInfo = 0;

fResult = CryptMsgGetParam(
            hMsg,                         // Handle to the message
            CMSG_SIGNER_CERT_INFO_PARAM,  // Parameter type
            0,                            // Index
            NULL,                         // Address for returned info
            &cbSignerCertInfo);           // Size of the returned info
if(!fResult)
{
    AfxMessageBox("Verify SIGNER_CERT_INFO #1 failed");
    free(pbEncodedBlob);
    free(pbDecoded);
    return;
}

// Allocate memory.
PCERT_INFO    pSignerCertInfo = NULL;

pSignerCertInfo = (PCERT_INFO) malloc(cbSignerCertInfo);
if(NULL == pSignerCertInfo)
{
    AfxMessageBox("Verify memory allocation failed");
    free(pbEncodedBlob);
    free(pbDecoded);
    return;
}

// Get the message certificate information (CERT_INFO
// structure).
fResult = CryptMsgGetParam(
            hMsg,                         // Handle to the message
            CMSG_SIGNER_CERT_INFO_PARAM,  // Parameter type
            0,                            // Index
            pSignerCertInfo,              // Address for returned info
            &cbSignerCertInfo);           // Size of the returned info
if(!fResult)
{
    AfxMessageBox("Verify SIGNER_CERT_INFO #2 failed");
    free(pbEncodedBlob);
    free(pbDecoded);
    free(pSignerCertInfo);
    return;
}

//-------------------------------------------------------------
// Open a certificate store in memory using CERT_STORE_PROV_MSG,
// which initializes it with the certificates from the message.
//-------------------------------------------------------------
HCERTSTORE    hTempStore;

hTempStore = CertOpenStore(
               CERT_STORE_PROV_MSG,         // Store Provider type 
               MY_ENCODING_TYPE,            // Encoding type
               hCryptProv,                  // Cryptographic provider
               0,                           // Flags
               hMsg);                       // Handle to the message
if(NULL == hTempStore)
{
    AfxMessageBox("Verify OpenStore failed");
    free(pbEncodedBlob);
    free(pbDecoded);
    free(pSignerCertInfo);
    return;
}

//-------------------------------------------------------------
// Find the signer's certificate in the store.
//-------------------------------------------------------------
PCCERT_CONTEXT    pSignerCertContext = NULL;

pSignerCertContext = CertGetSubjectCertificateFromStore(
                         hTempStore,            // Handle to store
                         MY_ENCODING_TYPE,      // Encoding type
                         pSignerCertInfo);      // Pointer to
                                                //   retrieved 
                                                //   CERT_CONTEXT
if(NULL == pSignerCertContext)
{
    AfxMessageBox("Verify GetSubjectCert failed");
    free(pbEncodedBlob);
    free(pbDecoded);
    free(pSignerCertInfo);
    CertCloseStore(hTempStore, CERT_CLOSE_STORE_FORCE_FLAG);
    return;
}

//-------------------------------------------------------------
// Use the CERT_INFO from the signer's certificate to verify
// the signature.
//-------------------------------------------------------------
PCERT_INFO pSignerCertificateInfo = pSignerCertContext->pCertInfo;

fResult = CryptMsgControl(
               hMsg,                          // Handle to the message
               0,                             // Flags
               CMSG_CTRL_VERIFY_SIGNATURE,    // Control type
               pSignerCertificateInfo);       // Pointer to the 
                                              //   retrieved CERT_INFO
if(!fResult)
{
    AfxMessageBox("Verify Signature failed");
    free(pbEncodedBlob);
    free(pbDecoded);
    free(pSignerCertInfo);
    CertCloseStore(hTempStore, CERT_CLOSE_STORE_FORCE_FLAG);
    return;
}
else
    AfxMessageBox("Verify Signature Succeeded");

//-------------------------------------------------------------
// Clean up.
//-------------------------------------------------------------
free(pbEncodedBlob);
free(pbDecoded);
free(pSignerCertInfo);
CertCloseStore(hTempStore, CERT_CLOSE_STORE_FORCE_FLAG);
CryptMsgClose(hMsg);

}


//************************************************************
// GetMySignerCert() - A function to list all the certs in the
// store and get a handle to one of them.
//************************************************************
PCCERT_CONTEXT GetMySignerCert(HCERTSTORE hCertStore)
{
    PCCERT_CONTEXT    pPrevCertContext = NULL;
    PCCERT_CONTEXT    pCertContext = NULL;
    BOOL              fReturnFlag = FALSE;
    BOOL              fTestFlag;
    DWORD             dwSize = NULL;
    KEY_PROV_INFO*    pKeyInfo = NULL;

    while(TRUE)
    {
        pCertContext = CertEnumCertificatesInStore(
                       hCertStore,
                       pPrevCertContext);
        if(!pCertContext)
        {
            fTestFlag = FALSE;
            break;
        }
        else
        {
            // For clarity, this code searches for the first
            // occurrence of a certificate containing an exchange key.
            // This works here, as there is only one pair of 
            // certificates in the store. Normally you would search
            // for a name as well as the key type.

            // Call CertGetCertificateContextProperty to get the
            // structure size.

            fReturnFlag = CertGetCertificateContextProperty(
                           pCertContext,
                           CERT_KEY_PROV_INFO_PROP_ID,
                           NULL,
                           &dwSize);
            if(FALSE == fReturnFlag)
                AfxMessageBox("Error Getting Key Property");

            // Allocate memory for the returned structure.
            if(pKeyInfo)
                free(pKeyInfo);
            pKeyInfo = (KEY_PROV_INFO*)malloc(dwSize);
            if(!pKeyInfo)
                AfxMessageBox("Error Allocating Memory");

            // Get the key info structure.
            fReturnFlag = CertGetCertificateContextProperty(
                          pCertContext,
                          CERT_KEY_PROV_INFO_PROP_ID,
                          pKeyInfo,
                          &dwSize);

            // Does it have a signature key?
            if(pKeyInfo->dwKeySpec == AT_SIGNATURE)
            {
                AfxMessageBox("Signature Cert Found");
                fTestFlag = TRUE;
                break;
            }

        }
        pPrevCertContext = pCertContext;    
    }
    CertFreeCertificateContext(pPrevCertContext);
    if(pKeyInfo)
        free(pKeyInfo);

    if(FALSE == fTestFlag)
    {
        AfxMessageBox("Signature Cert Not Found");
        return NULL;
    }else
        return (pCertContext);
}