Example Code For Countersigning a Message

The following example code creates a signed message, adds a countersignature to it, and then verifies the countersignature.

//===============================================================
// EXAMPLE CODE FOR CREATING A SIGNED MESSAGE, COUNTERSIGNING THE
// SIGNED MESSAGE, AND VERIFYING THE COUNTERSIGNATURE.
//===============================================================

//===============================================================
// CREATE 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, 
// usually the content will 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

// Display example name.
MessageBox(hwnd, "CryptMsgCountersign Example", NULL, MB_OK);

//-------------------------------------------------------------
// 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)
{
    MessageBox(hwnd, "CryptAcquireContext failed", NULL, MB_OK);
    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)
{
    MessageBox(hwnd, "Error Getting Store Handle", NULL, MB_OK);
    return;
}

// If the call was successful, the cert store handle now resides
// at the location pointed to by hStoreHandle.

//-------------------------------------------------------------
// Get a pointer to the signer's signature certificate.
//-------------------------------------------------------------
PCCERT_CONTEXT    pSignerCert = NULL;

// For the implementation of GetMySignerCert(), see the function
// at the end of this example code.
pSignerCert = GetMySignerCert(hwnd, hStoreHandle);
if(!pSignerCert)
{
    MessageBox(hwnd, "Error Getting Signer Cert", NULL, MB_OK);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    return;
}
else
    MessageBox(hwnd, "Signer's Certificate Found", NULL, MB_OK);

//-------------------------------------------------------------
// 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)
{
    MessageBox(hwnd, "Getting cbEncodedBlob length failed",
                NULL, MB_OK);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    return;
}

//-------------------------------------------------------------
// Allocate memory for the encoded blob.
//-------------------------------------------------------------
pbEncodedBlob = (BYTE *) malloc(cbEncodedBlob);
if(NULL == pbEncodedBlob)
{
    MessageBox(hwnd, "Malloc failed", NULL, MB_OK);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    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)
{
    MessageBox(hwnd, "OpenToEncode failed", NULL, MB_OK);
    free(pbEncodedBlob);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    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)
{
    MessageBox(hwnd, "MsgUpdate failed", NULL, MB_OK);
    free(pbEncodedBlob);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    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)
{
    MessageBox(hwnd, "MsgGetParam failed", NULL, MB_OK);
    free(pbEncodedBlob);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}else
    MessageBox(hwnd, "Message successfully signed", NULL, MB_OK);

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

CryptMsgClose(hMsg); // The message is complete - close the handle


//===============================================================
// COUNTERSIGN THE SIGNED MESSAGE. Assume that the message just
// created was sent to the intended recipient, and that a pointer
// (pbEncodedBlob) to the message (encoded blob) has been retrieved.
// The following code will add a countersignature to the signed 
// message.
//===============================================================

//---------------------------------------------------------------
// Before countersigning, the message must first be decoded.
// 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)
{
    MessageBox(hwnd, "OpenToDecode failed", NULL, MB_OK);
    free(pbEncodedBlob);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

//-------------------------------------------------------------
// Update the message with the data (encoded blob).
// In this example, pbEncodedBlob and cbEncodedBlob were
// initialized in the previous 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)
{
    MessageBox(hwnd, "Decode MsgUpdate failed", NULL, MB_OK);
    free(pbEncodedBlob);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    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)
{
    MessageBox(hwnd, "Decode CMSG_CONTENT_PARAM failed", NULL, MB_OK);
    free(pbEncodedBlob);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

//-------------------------------------------------------------
// Allocate memory.
//-------------------------------------------------------------
BYTE    *pbDecoded = NULL;

pbDecoded = (BYTE *) malloc(cbDecoded);
if(NULL == pbDecoded)
{
    MessageBox(hwnd, "Decode memory allocation failed",
                   NULL, MB_OK);
    free(pbEncodedBlob);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

//-------------------------------------------------------------
// Get a pointer to the content.
//-------------------------------------------------------------    
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)
{
    MessageBox(hwnd, "Decode CMSG_CONTENT_PARAM #2 failed",
                     NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}else
    MessageBox(hwnd, "Message decoded successfully", NULL, MB_OK);

//---------------------------------------------------------------
// 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 countersigner's certificate.
//---------------------------------------------------------------
PCCERT_CONTEXT    pCntrSigCert = NULL;

// For the implementation of GetMySignerCert(), see the function
// at the end of this example code.
pCntrSigCert = GetMySignerCert(hwnd, hStoreHandle);

if(!pCntrSigCert)
{
    MessageBox(hwnd, "Couldn't Find Countersigner's "
                     "Certificate", NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}
else
    MessageBox(hwnd, "Countersigner's Certificate Found",
                     NULL, MB_OK);

// If the call was successful, a pointer to the desired
// certificate now resides at pCntrSigCert.

//---------------------------------------------------------------
// Initialize the PCMSG_SIGNER_ENCODE_INFO structure.
//---------------------------------------------------------------
CMSG_SIGNER_ENCODE_INFO CountersignerInfo;

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_SIGNATURE;
CountersignerInfo.HashAlgorithm = HashAlgorithm;

CMSG_SIGNER_ENCODE_INFO CntrSignArray[1];
CntrSignArray[0] = CountersignerInfo;

//--------------------------------------------------------------
// Countersign the message.
//--------------------------------------------------------------
fReturn = CryptMsgCountersign(
                hMsg,
                0,
                1,
                CntrSignArray);
if(!fReturn)
{
    MessageBox(hwnd, "Countersign Failed", NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}
else
    MessageBox(hwnd, "Countersign Succeeded", NULL, MB_OK);

//---------------------------------------------------------------
// Get a pointer to the new, countersigned message blob.
//---------------------------------------------------------------

// Get the size of memory required.
fResult = CryptMsgGetParam(
                hMsg,                  // Handle to the message
                CMSG_ENCODED_MESSAGE,  // Parameter type
                0,                     // Index
                NULL,                  // Address for returned info
                &cbEncodedBlob);       // Size of the returned info
if(!fResult)
{
    MessageBox(hwnd, "Sizing of cbSignerInfo Failed", NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

// Allocate memory.
pbEncodedBlob = (PBYTE) malloc(cbEncodedBlob);
if(NULL == pbEncodedBlob)
{
    MessageBox(hwnd, "cbSignerInfo Memory Allocation Failed",
                      NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

// Get the new message encoded blob.
fResult = CryptMsgGetParam(
                hMsg,                  // Handle to the message
                CMSG_ENCODED_MESSAGE,  // Parameter type
                0,                     // Index
                pbEncodedBlob,             // Address for returned info
                &cbEncodedBlob);       // Size of the returned info
if(!fResult)
{
    MessageBox(hwnd, "Getting pbSignerInfo failed", NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

CryptMsgClose(hMsg); // The message is complete - close the handle

//=============================================================
// VERIFY THE COUNTERSIGNATURE. Assume that the countersigned 
// message was re-encoded, and sent back to the originator, where
// it will be decoded.
//=============================================================

//---------------------------------------------------------------
// Before verifying the countersignature, the message must first
// be decoded.
//
// 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)
{
    MessageBox(hwnd, "OpenToDecode For Verify Countersignature "
                      "Failed", NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

//-------------------------------------------------------------
// Update the message with the data (encoded blob).
// In this example, pbEncodedBlob and cbEncodedBlob were
// initialized in the previous 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)
{
    MessageBox(hwnd, "Verify Countersignature MsgUpdate Failed",
                       NULL, MB_OK);
    free(pbEncodedBlob);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

//-------------------------------------------------------------
// Get a pointer to the CERT_INFO member of countersigner's 
// certificate.  In this case, the certificate retrieved in the
// previous code segment will be used (pCntrSigCert).
//-------------------------------------------------------------

//--------------------------------------------------------------
// Retrieve the SignerInfo from the message.
//--------------------------------------------------------------

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

fResult = CryptMsgGetParam(
                hMsg,                  // Handle to the message
                CMSG_ENCODED_SIGNER,   // Parameter type
                0,                     // Index
                NULL,                  // Address for returned info
                &cbSignerInfo);        // Size of the returned info
if(!fResult)
{
    MessageBox(hwnd, "Sizing of cbSignerInfo Failed", NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

// Allocate memory.
PBYTE pbSignerInfo;

pbSignerInfo = (PBYTE) malloc(cbSignerInfo);
if(NULL == pbSignerInfo)
{
    MessageBox(hwnd, "cbSignerInfo Memory Allocation Failed",
                       NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

// Get the message signer information.
fResult = CryptMsgGetParam(
                hMsg,                 // Handle to the message
                CMSG_ENCODED_SIGNER,  // Parameter type
                0,                    // Index
                pbSignerInfo,         // Address for returned info
                &cbSignerInfo);       // Size of the returned info
if(!fResult)
{
    MessageBox(hwnd, "Getting pbSignerInfo failed", NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    free(pbSignerInfo);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

//--------------------------------------------------------------
// Retrieve the CountersignerInfo from the message.
//--------------------------------------------------------------

// Get the size of memory required.
DWORD    cbCountersignerInfo = 0;
PCRYPT_ATTRIBUTES   pCountersignerInfo = NULL;

fResult = CryptMsgGetParam(
                hMsg,                    // Handle to the message
                CMSG_SIGNER_UNAUTH_ATTR_PARAM,    // Parameter type
                0,                        // Index
                NULL,                    // Address for returned info
                &cbCountersignerInfo);    // Size of the returned info
if(!fResult)
{
    MessageBox(hwnd, "Sizing of cbCountersignerInfo Failed",
                      NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

// Allocate memory.
pCountersignerInfo = (PCRYPT_ATTRIBUTES) malloc(cbCountersignerInfo);
if(NULL == pCountersignerInfo)
{
    MessageBox(hwnd, "pbCountersignInfo Memory Allocation Failed", 
                      NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

// Get the message SIGNER_INFO.
fResult = CryptMsgGetParam(
                hMsg,                    // Handle to the message
                CMSG_SIGNER_UNAUTH_ATTR_PARAM,    // Parameter type
                0,                        // Index
                pCountersignerInfo,        // Address for returned info
                &cbCountersignerInfo);    // Size of the returned info
if(!fResult)
{
    MessageBox(hwnd, "Getting pbCountersignerInfo failed",
                      NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    free(pbSignerInfo);
    free(pCountersignerInfo);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}

//---------------------------------------------------------------
// Verify the countersignature.
//---------------------------------------------------------------
fReturn = CryptMsgVerifyCountersignatureEncoded(
                hCryptProv,
                MY_ENCODING_TYPE,
                pbSignerInfo,
                cbSignerInfo,
                pCountersignerInfo->rgAttr->rgValue->pbData,
                pCountersignerInfo->rgAttr->rgValue->cbData,
                pCntrSigCert->pCertInfo);
if(!fReturn)
{
    MessageBox(hwnd, "Verify Countrsignature Failed",
                      NULL, MB_OK);
    free(pbEncodedBlob);
    free(pbDecoded);
    free(pbSignerInfo);
    free(pCountersignerInfo);
    CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
    CryptMsgClose(hMsg);
    return;
}
else
    MessageBox(hwnd, "Verify Countrsignature Succeeded",
                      NULL, MB_OK);

//--------------------------------------------------------------
// Clean up.
//--------------------------------------------------------------
free(pbEncodedBlob);
free(pbDecoded);
free(pbSignerInfo);
free(pCountersignerInfo);
CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
CryptMsgClose(hMsg);

//************************************************************
// GetMySignerCert() - A function to list all the certificates
// in the store and get a handle to one of them.
//************************************************************
PCCERT_CONTEXT GetMySignerCert(HWND hwnd, HCERTSTORE hCertStore)
{
    PCCERT_CONTEXT         pPrevCertContext = NULL;
    PCCERT_CONTEXT         pCertContext = NULL;
    BOOL                   fReturnFlag = FALSE;
    BOOL                   fTestFlag;
    DWORD                  dwSize = NULL;
    CRYPT_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
            // struct size.

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

            // Allocate memory for the returned structure.
            if(pKeyInfo)
                free(pKeyInfo);
            pKeyInfo = (CRYPT_KEY_PROV_INFO*)malloc(dwSize);
            if(!pKeyInfo)
            {
                AfxMessageBox("Error Allocating Memory");
                fTestFlag = FALSE;
                break;
            }

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

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

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

    if(FALSE == fTestFlag)
    {
        MessageBox(hwnd, "Signature Cert Not Found",
                        NULL, MB_OK);
        return NULL;
    }else
        return (pCertContext);
}