Alternate Code for Encoding an Enveloped Message
The following example demonstrates an alternate process of encoding a signed message, using that signed message as the inner content for an enveloped message. In preparation for decoding, the inner content is tested to determine its inner-content type.
This example illustrates the following CryptoAPI functions:
- CryptAcquireContext
- CertOpenSystemStore
- CryptMsgCalculateEncodedLength
- CryptMsgOpenToEncode
- CryptMsgUpdate
- CryptMsgGetParam
- CryptMsgOpenToDecode
- CertFindCertificateInStore
- CryptMsgClose
- CertCloseStore
- CryptReleaseContext
This example also uses the functions MyHandleError and GetSignerCert. C code for these functions is included with the example. For code that demonstrates these and other auxiliary functions, see General Purpose Functions.
//-------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. // In this and all other examples, // use the #define and // #include statements listed under #includes and #defines. #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); //-------------------------------------------------------------------- // This program uses the function GetSignerCert, declared here and // defined after main. PCCERT_CONTEXT GetSignerCert( HCERTSTORE hCertStore); void main(void) { //-------------------------------------------------------------------- // 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 = (BYTE*)"The message to be countersigned."; // The message DWORD cbContent; // Size of message HCRYPTPROV hCryptProv; // CSP handle HCERTSTORE hStoreHandle; // Store handle PCCERT_CONTEXT pSignerCert; // Signer certificate DWORD HashAlgSize; CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; 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; //-------------------------------------------------------------------- // Begin processing. cbContent = strlen((char *) pbContent)+1; // One is added to include the final NULL character. printf("Processing begins.\n"); printf("The length of the original message is %d.\n",cbContent); printf("Example message:->%s\n", pbContent); //-------------------------------------------------------------------- // Get a handle to a cryptographic provider. if(CryptAcquireContext( &hCryptProv, // Address for handle to be returned NULL, // Use the logon name for the current user NULL, // Use the default provider PROV_RSA_FULL, // Provider type 0)) // Zero allows access to private keys { printf("The CSP has been opened."); } else { MyHandleError("CryptAcquireContext failed"); } //-------------------------------------------------------------------- // Open the MY system certificate store. if(hStoreHandle = CertOpenStore( CERT_STORE_PROV_SYSTEM, // The system store will be a // virtual store. 0, // Encoding type not needed with // this PROV. NULL, // Accept the default HCRYPTPROV. CERT_SYSTEM_STORE_CURRENT_USER, // Set the system store location in the // registry. L"MY")) // Other predefined system stores // could have been used, // including trust, CA, or root. { printf("Opened the MY system store. \n"); }else { MyHandleError( "Could not open the MY system store."); } //-------------------------------------------------------------------- // Get a pointer to the signature certificate of the signer. if(pSignerCert = GetSignerCert(hStoreHandle)) { printf("A signer certificate was found. \n"); } else { MyHandleError("Error getting signer certificate."); } //-------------------------------------------------------------------- // 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_SIGNER_ENCODE_INFO structure. 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_KEYEXCHANGE; SignerEncodeInfo.HashAlgorithm = HashAlgorithm; SignerEncodeInfo.pvHashAuxInfo = NULL; //-------------------------------------------------------------------- // Initialize the first element of an array of signers. // There can be 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. // Only one signer BLOB 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; SignedMsgEncodeInfo.rgCrlEncoded = NULL; //-------------------------------------------------------------------- // Get the size of the encoded message BLOB. if(cbEncodedBlob = CryptMsgCalculateEncodedLength( MY_ENCODING_TYPE, // Message encoding type 0, // Flags CMSG_SIGNED, // Message type &SignedMsgEncodeInfo, // Pointer to structure NULL, // Inner content OID cbContent)) // Size of content { printf("The size for the encoded BLOB is %d.\n",cbEncodedBlob); } else { MyHandleError("Getting cbEncodedBlob length failed."); } //-------------------------------------------------------------------- // Allocate memory for the encoded BLOB. if(pbEncodedBlob = (BYTE *) malloc(cbEncodedBlob)) { printf("Memory has been allocated for the BLOB. \n"); } else { MyHandleError("Malloc operation failed."); } //-------------------------------------------------------------------- // Open a message to encode. if(hMsg = CryptMsgOpenToEncode( MY_ENCODING_TYPE, // Encoding type 0, // Flags CMSG_SIGNED, // Message type &SignedMsgEncodeInfo, // Pointer to structure NULL, // Inner content OID NULL)) // Stream information (not used) { printf("The message to encode is open. \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("Message to encode has been updated. \n"); } else { MyHandleError("MsgUpdate failed"); } //-------------------------------------------------------------------- // Get the resulting message. if(CryptMsgGetParam( hMsg, // Handle to the message CMSG_CONTENT_PARAM, // Parameter type 0, // Index pbEncodedBlob, // Pointer to the BLOB &cbEncodedBlob)) // Size of the BLOB { printf("Message successfully signed. \n"); } else { MyHandleError("MsgGetParam failed."); } //-------------------------------------------------------------------- // pbEncodedBlob points to the encoded, signed content. // Include any further processing here. CryptMsgClose(hMsg); // The message is complete--close the handle. //-------------------------------------------------------------------- // Next, countersign the signed message. // Assume that the message just created and that a pointer // (pbEncodedBlob) to the message were sent to the intended // recipient. // The following code, from the recipient's point of view, adds a // countersignature to the signed message. // // Before countersigning, the message must be decoded. //-------------------------------------------------------------------- // Open a 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 for decoding has been opened. \n"); } else { MyHandleError("OpenToDecode failed."); } //-------------------------------------------------------------------- // Update the message with the data (encoded BLOB). // In this example, pbEncodedBlob and cbEncodedBlob were // created in the previous code. if(CryptMsgUpdate( hMsg, // Handle to the message pbEncodedBlob, // Pointer to the encoded BLOB cbEncodedBlob, // Size of the encoded BLOB TRUE)) // Last call { printf("The message to be decoded has been updated. \n"); } else { MyHandleError("Decode MsgUpdate failed."); } //-------------------------------------------------------------------- // 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 message to be decoded is %d bytes long. \n", cbDecoded); } else { MyHandleError("Decode CMSG_CONTENT_PARAM failed"); } //-------------------------------------------------------------------- // Allocate memory. if(pbDecoded = (BYTE *) malloc(cbDecoded)) { printf("Memory has been allocated. \n"); } else { MyHandleError("Decode memory allocation failed"); } //-------------------------------------------------------------------- // Get a pointer to the content. 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("The successfully decoded message is =>%s\n",pbDecoded); } else { MyHandleError("Decode CMSG_CONTENT_PARAM #2 failed."); } //-------------------------------------------------------------------- // 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 certificate of the countersigner. A certificate with a // subject name that matches the string in parameter five must // be in the MY store and must have its CERT_KEY_PROV_INFO_PROP_ID // property set. if(pCntrSigCert=CertFindCertificateInStore( hStoreHandle, MY_ENCODING_TYPE, // Use X509_ASN_ENCODING. 0, // No dwFlags needed. CERT_FIND_SUBJECT_STR, // Find a certificate with a // subject that matches the string // in the next parameter. L"Full Test Cert", // The Unicode string to be found // in a certificate's subject. NULL)) // NULL for the first call to the // function. In all subsequent // calls, it is the last pointer // returned by the function. { printf("The desired certificate was found. \n"); } else { MyHandleError("Could not find the countersigner's certificate."); } //-------------------------------------------------------------------- // Initialize the PCMSG_SIGNER_ENCODE_INFO structure. 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_KEYEXCHANGE; CountersignerInfo.HashAlgorithm = HashAlgorithm; CntrSignArray[0] = CountersignerInfo; //-------------------------------------------------------------------- // Countersign the message. if(CryptMsgCountersign( hMsg, 0, 1, CntrSignArray)) { printf("Countersign succeeded. \n"); } else { MyHandleError("Countersign failed."); } //-------------------------------------------------------------------- // Get a pointer to the new, countersigned message BLOB. // Get the size of memory required. if(CryptMsgGetParam( hMsg, // Handle to the message CMSG_ENCODED_MESSAGE, // Parameter type 0, // Index NULL, // Address for returned information &cbEncodedBlob)) // Size of the returned information { printf("The size for the encoded BLOB is %d.\n",cbEncodedBlob); } else { MyHandleError("Sizing of cbSignerInfo failed."); } //-------------------------------------------------------------------- // Allocate memory. if(pbEncodedBlob = (BYTE*) malloc(cbEncodedBlob)) { printf("%d bytes allocated .\n", cbEncodedBlob); } else { MyHandleError("cbSignerInfo memory allocation failed"); } //-------------------------------------------------------------------- // Get the new message encoded BLOB. if(CryptMsgGetParam( hMsg, // Handle to the message CMSG_ENCODED_MESSAGE, // Parameter type 0, // Index pbEncodedBlob, // Address for returned information &cbEncodedBlob)) // Size of the returned information { 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, // Encoding type 0, // Flags 0, // Message type (get from message) hCryptProv, // Cryptographic provider NULL, // Recipient information NULL)) // Stream information { printf("The message to decode has been opened. \n"); } else { MyHandleError("OpenToDecode failed."); } //-------------------------------------------------------------------- // Update the message with the encoded BLOB. // In this example, pbEncodedBlob and cbEncodedBlob were // initialized in the previous code. if(CryptMsgUpdate( hMsg, // Handle to the message pbEncodedBlob, // Pointer to the encoded BLOB cbEncodedBlob, // Size of the encoded BLOB TRUE)) // Last call { printf("The message to decode has been updated. \n"); } else { MyHandleError("Updating of the verified countersignature " "message failed."); } //------------------------------------------------------------- // Get a pointer to the CERT_INFO member of the certificate of the // countersigner. In this case, the certificate retrieved in the // previous code segment will be used (pCntrSigCert). //-------------------------------------------------------------- // Retrieve the signer information from the message. // Get the size of memory required. if(CryptMsgGetParam( hMsg, // Handle to the message CMSG_ENCODED_SIGNER, // Parameter type 0, // Index NULL, // Address for returned information &cbSignerInfo)) // Size of the returned information { printf("The size of the signer information has been " "retrieved.\n"); } else { MyHandleError("Sizing of cbSignerInfo failed."); } //-------------------------------------------------------------------- // Allocate memory. if(pbSignerInfo = (BYTE*) malloc(cbSignerInfo)) { printf("%d bytes allocated for the signer " "information. \n", cbSignerInfo); } else { MyHandleError("cbSignerInfo memory allocation failed"); } //-------------------------------------------------------------------- // Get the message signer information. if(CryptMsgGetParam( hMsg, // Handle to the message CMSG_ENCODED_SIGNER, // Parameter type 0, // Index pbSignerInfo, // Address for returned information &cbSignerInfo)) // Size of the returned information { printf("The signer information is retrieved. \n"); } else { MyHandleError("Getting pbSignerInfo failed."); } //-------------------------------------------------------------------- // Retrieve the countersigner information from the message. // Get the size of memory required. if(CryptMsgGetParam( hMsg, // Handle to the message CMSG_SIGNER_UNAUTH_ATTR_PARAM,// Parameter type 0, // Index NULL, // Address for returned // information &cbCountersignerInfo)) // Size of returned // information { printf("The length of the countersigner's information is " "retrieved. \n"); } else { MyHandleError("Sizing of cbCountersignerInfo failed."); } //-------------------------------------------------------------------- // Allocate memory. if(pCountersignerInfo = (CRYPT_ATTRIBUTES*)malloc(cbCountersignerInfo)) { printf("%d bytes allocated. \n", cbCountersignerInfo); } else { MyHandleError("pbCountersignInfo memory allocation failed."); } //-------------------------------------------------------------------- // Get the message SIGNER_INFO. if(CryptMsgGetParam( hMsg, // Handle to the message CMSG_SIGNER_UNAUTH_ATTR_PARAM, // Parameter type 0, // Index pCountersignerInfo, // Address for returned // information &cbCountersignerInfo)) // Size of the returned // information { printf("Countersigner information retrieved. \n"); } else { MyHandleError("Getting pbCountersignerInfo failed."); } //-------------------------------------------------------------------- // Verify the countersignature. if(CryptMsgVerifyCountersignatureEncoded( hCryptProv, 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"); } //-------------------------------------------------------------------- // Clean up. free(pbEncodedBlob); free(pbDecoded); free(pbSignerInfo); free(pCountersignerInfo); CertCloseStore( hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG); CryptMsgClose(hMsg); CryptReleaseContext(hCryptProv,0); } // End of main //-------------------------------------------------------------------- // 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); } // GetSignerCert enumerates the certificates in a store and // finds the first certificate that has a signature key. If a // certificate is found, a pointer to the certificate is returned. PCCERT_CONTEXT GetSignerCert( HCERTSTORE hCertStore) //-------------------------------------------------------------------- // Parameter passed in: // hCertStore, the handle of the store to be searched. { //-------------------------------------------------------------------- // Declare and initialize local variables. PCCERT_CONTEXT pCertContext = NULL; BOOL fMore = TRUE; DWORD dwSize = NULL; CRYPT_KEY_PROV_INFO* pKeyInfo = NULL; DWORD PropId = CERT_KEY_PROV_INFO_PROP_ID; //-------------------------------------------------------------------- // Find certificates in the store until the end of the store // is reached or a certificate with an AT_SIGNATURE key is found. while(fMore && (pCertContext= CertFindCertificateInStore( hCertStore, // Handle of the store to be searched. 0, // Encoding type. Not used for this search. 0, // dwFindFlags. Special find criteria. // Not used in this search. CERT_FIND_PROPERTY, // Find type. Determines the kind of // search to be done. In this case, search // for certificates that have a specific // extended property. &PropId, // pvFindPara. Gives the specific // value searched for, here the identifier // of an extended property. pCertContext))) // pCertContext is NULL for the // first call to the function. // If the function were being called // in a loop, after the first call, // pCertContext would be the certificate // returned by the previous call. { //------------------------------------------------------------- // For simplicity, this code only searches // for the first occurrence of an AT_SIGNATURE key. // In many situations, a search would also look for a // specific subject name as well as the key type. //------------------------------------------------------------- // Call CertGetCertificateContextProperty once to get the // returned structure size. if(!(CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwSize))) { MyHandleError("Error getting key property"); } //-------------------------------------------------------------- // Allocate memory for the returned structure. if(pKeyInfo) free(pKeyInfo); if(!(pKeyInfo = (CRYPT_KEY_PROV_INFO*)malloc(dwSize))) { MyHandleError("Error allocating memory for pKeyInfo"); } //-------------------------------------------------------------- // Get the key information structure. if(!(CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, pKeyInfo, &dwSize))) { MyHandleError("The second call to the function failed."); } //------------------------------------------- // Check the dwKeySpec member for a signature key. if(pKeyInfo->dwKeySpec == AT_SIGNATURE) { fMore = FALSE; } } // End of while loop if(pKeyInfo) free(pKeyInfo); return (pCertContext); } // End of GetSignerCert.