Example C Program: Encoding and Decoding a Hashed Message
The following example hashes and encodes a text message, and then decodes and verifies the message.
Although, for simplicity, the two different functions have been combined in this example, in a more realistic setting the two parts would be used separately.
This example illustrates the following tasks and CryptoAPI functions:
- Calling CryptAcquireContext to acquire a CSP provider.
- Using CryptMsgCalculateEncodedLength to calculate the length of the encoded message.
- Allocating memory for a buffer to hold the encoded data.
- Opening a message to encode using CryptMsgOpenToEncode.
- Adding content to the message to encode using CryptMsgUpdate.
- Using CryptMsgGetParam to copy the encoded message to the allocated buffer.
- Opening a message to decode using CryptMsgOpenToDecode.
- Adding the encoded message to the message to decode using CryptMsgUpdate.
- Creating a duplicate pointer to the message using CryptMsgDuplicate.
- Checking the message type with CryptMsgGetParam.
- Using CryptMsgGetParam to decode the message.
- Verifying the hash using CryptMsgControl.
- Using CryptMsgClose to release the message handle.
- Using CryptReleaseContext to release the CSP.
This example uses the function MyHandleError. Code for this function is included with the sample.
Code for this and other auxiliary functions is also listed under General Purpose Functions.
#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);
void main(void)
{
//-------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Declare and initialize variables. This includes creating a
// pointer to the message content. In real situations,
// the message content will usually exist somewhere and a pointer
// to it will get passed to the application.
BYTE* pbContent = (BYTE*) "A razzle-dazzle hashed message \n"
"Hashing is better than trashing. \n"; // The message
DWORD cbContent = strlen((char *)pbContent)+1; // Size of message
// including the
// final NULL.
HCRYPTPROV hCryptProv; // CSP handle
DWORD HashAlgSize;
CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
CMSG_HASHED_ENCODE_INFO HashedEncodeInfo;
DWORD cbEncodedBlob;
BYTE *pbEncodedBlob;
HCRYPTMSG hMsg;
HCRYPTMSG hDupMsg;
//-------------------------------------------------------------------
// Variables to be used in decoding.
DWORD cbData = sizeof(DWORD);
DWORD dwMsgType;
DWORD cbDecoded;
BYTE *pbDecoded;
//-------------------------------------------------------------------
// Begin processing.
printf("Begin processing. \n");
printf("The message to be hashed and encoded is: \n");
printf("%s\n",pbContent); // Display original message.
printf("The starting message length is %d\n",cbContent);
//-------------------------------------------------------------------
// Acquire a cryptographic provider context handle.
if(CryptAcquireContext(
&hCryptProv, // Address for the handle.
NULL, // Use the current user's logon name.
NULL, // Use the default provider.
PROV_RSA_FULL, // Provider type.
0)) // Zero allows access to
// private keys.
{
printf("A CSP context has been acquired. \n");
}
else
{
MyHandleError("CryptAcquireContext failed.");
}
//-------------------------------------------------------------------
// The function succeeded; hCryptProv is the CSP handle.
//-------------------------------------------------------------------
// 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_HASHED_ENCODE_INFO structure.
memset(&HashedEncodeInfo, 0, sizeof(CMSG_HASHED_ENCODE_INFO));
HashedEncodeInfo.cbSize = sizeof(CMSG_HASHED_ENCODE_INFO);
HashedEncodeInfo.hCryptProv = hCryptProv;
HashedEncodeInfo.HashAlgorithm = HashAlgorithm;
HashedEncodeInfo.pvHashAuxInfo = NULL;
//-------------------------------------------------------------------
// Get the size of the encoded message BLOB.
if(cbEncodedBlob = CryptMsgCalculateEncodedLength(
MY_ENCODING_TYPE, // Message encoding type
0, // Flags
CMSG_HASHED, // Message type
&HashedEncodeInfo, // Pointer to structure
NULL, // Inner content object ID
cbContent)) // Size of content
{
printf("The length to be allocated is %d bytes.\n",
cbEncodedBlob);
}
else
{
MyHandleError("Getting cbEncodedBlob length failed");
}
//-------------------------------------------------------------------
// Allocate memory for the encoded BLOB.
if(pbEncodedBlob = (BYTE *) malloc(cbEncodedBlob))
{
printf("%d bytes of memory have been allocated.\n",
cbEncodedBlob);
}
else
{
MyHandleError("Malloc operation failed.");
}
//-------------------------------------------------------------------
// Open a message to encode.
if(hMsg = CryptMsgOpenToEncode(
MY_ENCODING_TYPE, // Encoding type
0, // Flags
CMSG_HASHED, // Message type
&HashedEncodeInfo, // Pointer to structure
NULL, // Inner content object ID
NULL)) // Stream information (not used)
{
printf("The message to encode has been opened. \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("Data has been added to the message to encode. \n");
}
else
{
MyHandleError("MsgUpdate failed");
}
//-------------------------------------------------------------------
// Create a duplicate of the message.
if(hDupMsg = CryptMsgDuplicate(hMsg))
{
printf("The message has been duplicated.\n");
}
else
{
MyHandleError("Duplication of the message failed.");
}
//-------------------------------------------------------------------
// Get the resulting message from the duplicate of the message.
if(CryptMsgGetParam(
hDupMsg, // Handle to the message
CMSG_CONTENT_PARAM, // Parameter type
0, // Index
pbEncodedBlob, // Pointer to the BLOB
&cbEncodedBlob)) // Size of the BLOB
{
printf("Message encoded successfully. \n");
}
else
{
MyHandleError("MsgGetParam failed");
}
//-------------------------------------------------------------------
// Close both messages to prepare for decoding.
CryptMsgClose(hMsg);
CryptMsgClose(hDupMsg);
// The following code decodes the hashed message.
// Usually, this would be in a separate program and the encoded,
// hashed data would be input from a file, from an email message,
// or from some other source.
//
// The variables used in this code have already been
// declared and initialized.
//-------------------------------------------------------------------
// Open the 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 has been opened for decoding. \n");
}
else
{
MyHandleError("OpenToDecode failed");
}
//-------------------------------------------------------------------
// Update the message with the encoded BLOB.
if(CryptMsgUpdate(
hMsg, // Handle to the message
pbEncodedBlob, // Pointer to the encoded BLOB
cbEncodedBlob, // Size of the encoded BLOB
TRUE)) // Last call
{
printf("The encoded data is added to the message "
"to decode. \n");
}
else
{
MyHandleError("Decode MsgUpdate failed");
}
//-------------------------------------------------------------------
// Get the message type.
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_TYPE_PARAM, // Parameter type
0, // Index
&dwMsgType, // Address for returned information
&cbData)) // Size of the returned information
{
printf("The message type has been obtained. \n");
}
else
{
MyHandleError("Decode CMSG_TYPE_PARAM failed");
}
//-------------------------------------------------------------------
// Some applications may need to use a switch statement here
// and process the message differently, depending on the
// message type.
if(dwMsgType == CMSG_HASHED)
{
printf("The message is a hashed message. Proceed. \n");
}
else
{
MyHandleError("Wrong message type");
}
//-------------------------------------------------------------------
// 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 length %d of the message obtained. \n", cbDecoded);
}
else
{
MyHandleError("Decode CMSG_CONTENT_PARAM failed");
}
//-------------------------------------------------------------------
// Allocate memory.
if(pbDecoded = (BYTE *) malloc(cbDecoded))
{
printf("Memory for the decoded message has been allocated.\n");
}
else
{
MyHandleError("Decoding memory allocation failed");
}
//-------------------------------------------------------------------
// Copy the decoded message into the buffer just allocated.
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("Message decoded successfully \n");
printf("The decoded message is \n%s\n", (LPSTR)pbDecoded);
}
else
{
MyHandleError("Decoding CMSG_CONTENT_PARAM #2 failed");
}
//-------------------------------------------------------------------
// Verify the hash.
if(CryptMsgControl(
hMsg, // Handle to the message
0, // Flags
CMSG_CTRL_VERIFY_HASH, // Control type
NULL)) // Pointer not used
{
printf("Verification of hash succeeded. \n");
printf("The data has not been tampered with.\n");
}
else
{
printf("Verification of hash failed. Something changed "
"this message .\n");
}
printf("Test program completed without error. \n");
//-------------------------------------------------------------------
// Clean up
if(pbEncodedBlob)
free(pbEncodedBlob);
if(pbDecoded)
free(pbDecoded);
CryptMsgClose(hMsg);
// Release the CSP.
if(hCryptProv)
CryptReleaseContext(hCryptProv,0);
} // End of main
//-------------------------------------------------------------------
// This example uses the function MyHandleError, a simple error
// handling function, to print an error message to the standard
// error (stderr) file and exit the program.
// For most applications, replace this function with one
// that does more extensive error reporting.
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);
} // End of MyHandleError