範例 C 程式:使用資料流程編碼和解碼訊息
下列範例示範如何使用 CryptMsgOpenToEncode、 CryptMsgOpenToDecode和 CryptMsgUpdate 函式搭配 CMSG_STREAM_INFO 結構來編碼和解碼這些函式的串流功能。
簽署和編碼訊息無法確保該訊息的隱私權。 而是確保訊息的真確性。 由於訊息是以寄件者的私密金鑰簽署,因此當訊息的接收者使用傳送者的 公開金鑰 解密簽章時, (從連同訊息一起傳送的憑證) ,接收者可以確定訊息是由與憑證相關聯的人員或實體所傳送,而且訊息在簽署之後未變更。
此範例的這個編碼簽署部分說明下列工作和 CryptoAPI 函式:
- 使用 CertOpenStore開啟憑證存放區。
- 使用 CertFindCertificateInStore擷取具有特定主體名稱的憑證。
- 使用 CertGetNameString取得和列印憑證的主體名稱。
- 取得密碼編譯提供者的控制碼,該提供者可以使用 CryptAcquireCertificatePrivateKey 函 式來提供私密金鑰。
- 初始化要用於CryptMsgOpenToEncode呼叫中的CMSG_SIGNED_ENCODE_INFO和CMSG_STREAM_INFO結構。
- 使用 CryptMsgOpenToEncode 和 CryptMsgUpdate簽署和編碼訊息。
- 實作資料流程回呼函式,以任何持續性格式儲存編碼和已簽署的訊息,例如將它寫入檔案。
此範例的解碼部分說明下列工作和 CryptoAPI 函式:
- 初始化要用於CryptMsgOpenToDecode呼叫中的CMSG_STREAM_INFO結構。
- 實作資料流程回呼函式,以任何持續性格式儲存解碼的訊息,例如將它列印到螢幕。
- 從檔案讀取編碼的訊息,並使用 CryptMsgUpdate解碼訊息。
如需如何在不使用資料流程回呼的情況下執行這些相同作業的範例,請參閱 範例 C 程式:簽署、編碼、解碼和驗證訊息。
此範例會使用 MyHandleError函式。 此函式的程式碼隨附于範例中。 這個和其他輔助函式的程式碼也會列在 [General_Purpose_Functions] 底下。
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")
// Link with the Crypt32.lib file.
#pragma comment (lib, "Crypt32")
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define MAX_NAME 256
#define ENCODED_FILE_NAME L"testStream.p7s"
//-------------------------------------------------------------------
// Define function MyHandleError.
void MyHandleError(LPTSTR psz)
{
_ftprintf(stderr, TEXT("An error occurred in the program. \n"));
_ftprintf(stderr, TEXT("%s\n"), psz);
_ftprintf(stderr, TEXT("Error number %x.\n"), GetLastError());
_ftprintf(stderr, TEXT("Program terminating. \n"));
exit(1);
} // End of MyHandleError.
//+----------------------------------------------
// Callback function used for streamed Signing.
//-----------------------------------------------
BOOL
WINAPI
EncodeCallback(
const void *pvArg,
BYTE *pbData,
DWORD cbData,
BOOL fFinal)
{
DWORD dwWrittenBytes = 0;
HANDLE hFileToWrite = INVALID_HANDLE_VALUE;
hFileToWrite = *((HANDLE *)pvArg);
if ( !WriteFile(
hFileToWrite,
pbData,
cbData,
&dwWrittenBytes,
NULL) ||
(dwWrittenBytes != cbData))
{
return FALSE;
}
return TRUE;
}
//+----------------------------------------------
// Callback function used for decoding streamed Signing.
//-----------------------------------------------
BOOL
WINAPI
DecodeCallback(
const void *pvArg,
BYTE *pbData,
DWORD cbData,
BOOL fFinal)
{
if (pbData != NULL && cbData > 0)
{
*(pbData+cbData) = 0;
printf("%s", (char*)pbData);
}
return TRUE;
}
void EncodeMessageWithStream(LPWSTR pwszSignerName)
{
//---------------------------------------------------------------
// 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* pbContent1 = (BYTE*)"First sentence. ";
DWORD cbContent1 = lstrlenA((char *)pbContent1);
BYTE* pbContent2 = (BYTE*)"Second sentence. ";
DWORD cbContent2 = lstrlenA((char *)pbContent2);
HCRYPTPROV hCryptProv; // CSP handle
HCERTSTORE hStoreHandle; // store handle
PCCERT_CONTEXT pSignerCert; // signer certificate
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo;
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfoArray[1];
CERT_BLOB SignerCertBlob;
CERT_BLOB SignerCertBlobArray[1];
CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo;
HCRYPTMSG hMsg;
LPWSTR pszNameString;
DWORD dwKeySpec;
//---------------------------------------------------------------
// Open the My system certificate store.
if(!(hStoreHandle = CertOpenStore(
// The system store will be a virtual store.
CERT_STORE_PROV_SYSTEM,
// Encoding type not needed with this PROV.
0,
// Accept the default HCRYPTPROV.
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
// Set the system store location in the registry. Other
// predefined system stores could have been used, including
// trust, Ca, or root.
L"MY")))
{
MyHandleError(L"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,
pwszSignerName,
NULL))
{
//-----------------------------------------------------------
// A certificate was found. Get and print the name of the
// subject of the certificate.
if(CertGetNameString(
pSignerCert,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
pszNameString,
MAX_NAME) > 1)
{
printf("The message signer is %s \n",pszNameString);
}
else
{
MyHandleError(L"CertGetNameString failed.\n");
}
}
else
{
MyHandleError(L"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)))
{
DWORD dwError = GetLastError();
if(NTE_BAD_PUBLIC_KEY == dwError)
{
printf("NTE_BAD_PUBLIC_KEY\n");
}
MyHandleError(L"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, there is 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;
// Fill the CMSG_STREAM_INFO structure.
CMSG_STREAM_INFO stStreamInfo;
// BER_ENCODING
stStreamInfo.cbContent = 0xffffffff;
// DER_ENCODING
// stStreamInfo.cbContent = cbContent;
stStreamInfo.pfnStreamOutput = EncodeCallback;
HANDLE hOutMsgFile = INVALID_HANDLE_VALUE;
hOutMsgFile = CreateFile(
ENCODED_FILE_NAME,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE == hOutMsgFile)
{
MyHandleError(L"CreateFile (OUT MSG)");
}
stStreamInfo.pvArg = &hOutMsgFile;
//---------------------------------------------------------------
// 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
&stStreamInfo))) // stream information
{
MyHandleError(L"OpenToEncode failed");
}
//---------------------------------------------------------------
// Update the message with the data.
if(!(CryptMsgUpdate(
hMsg, // handle to the message
pbContent1, // pointer to the content
cbContent1, // size of the content
FALSE))) // first call
{
MyHandleError(L"MsgUpdate failed");
}
if(!(CryptMsgUpdate(
hMsg, // handle to the message
pbContent2, // pointer to the content
cbContent2, // size of the content
TRUE))) // last call
{
MyHandleError(L"MsgUpdate 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);
CloseHandle(hOutMsgFile);
}
void DecodeMessageWithStream()
{
//---------------------------------------------------------------
// Open the message for decoding.
HCRYPTMSG hMsg;
// Fill the CMSG_STREAM_INFO structure.
CMSG_STREAM_INFO stStreamInfo2;
// BER_ENCODING
stStreamInfo2.cbContent = 0xffffffff;
stStreamInfo2.pfnStreamOutput = DecodeCallback;
if(!(hMsg = CryptMsgOpenToDecode(
MY_ENCODING_TYPE, // encoding type
0, // flags
0, // message type (get from message)
NULL, // cryptographic provider
// use NULL for the default provider
NULL, // recipient information
&stStreamInfo2))) // stream information
{
MyHandleError(L"OpenToDecode failed.");
}
HANDLE hInMsgFile = INVALID_HANDLE_VALUE;
hInMsgFile = CreateFile(
ENCODED_FILE_NAME,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE == hInMsgFile)
{
MyHandleError(L"CreateFile (IN MSG)");
}
const DWORD cbBytesToRead = 256;
byte pbEncodedBlob[cbBytesToRead];
DWORD cbBytesRead;
BOOL lastCall = FALSE;
while (ReadFile(
hInMsgFile,
pbEncodedBlob,
cbBytesToRead,
&cbBytesRead,
NULL))
{
if (cbBytesRead < cbBytesToRead)
{
lastCall = TRUE;
}
if(!(CryptMsgUpdate(
hMsg, // handle to the message
pbEncodedBlob, // pointer to the encoded BLOB
cbBytesRead, // size of the encoded BLOB
lastCall))) // last call
{
MyHandleError(L"Decode MsgUpdate failed.");
}
if (lastCall)
{
break;
}
}
CryptMsgClose(hMsg);
CloseHandle(hInMsgFile);
}