示例 C 程序:对消息进行签名、编码、解码和验证
以下示例组合了对消息进行签名和编码,以及解码已签名的消息并验证签名。 这两个操作通常位于单独的程序中。 编码示例将创建编码的消息,将其保存到磁盘文件,或者以某种其他方式将其发送给其他用户。 解码示例将接收编码的消息,对其进行解码并验证签名。 此处已合并这两个过程,以显示这两个过程都有效。
对消息进行签名和编码并不能确保该消息的隐私。 相反,它可确保消息的真实性。 由于消息是使用发送方的私钥签名的,因此当消息接收方使用发送方的 公钥 解密签名时, (随消息) 一起发送的证书中提供,接收方可以确保消息是由与证书关联的个人或实体发送的,并且消息在签名后未更改。
此示例演示了以下用于对消息进行编码的任务和 CryptoAPI 函数:
- 使用 CertOpenStore 打开证书存储。
- 使用 CertFindCertificateInStore 检索具有特定使用者名称的证书。
- 使用 CertGetNameString 获取并打印证书的使用者名称。
- 初始化要用于调用 CryptSignMessage 的CRYPT_SIGN_MESSAGE_PARA结构。
- 使用 CryptSignMessage 对消息进行签名和编码。
此示例演示以下用于解码消息和验证签名的任务和 CryptoAPI 函数:
- 打开消息以使用 CryptMsgOpenToDecode 进行解码。
- 使用 CryptMsgUpdate 将编码的 BLOB 添加到要解码的消息中。
- 使用 CryptMsgGetParam 解码消息。
- 使用接收和解码的消息使用 CertOpenStore 在内存中打开证书存储。
- 使用 CertGetSubjectCertificateFromStore 获取消息签名者的证书。
- 使用 CryptMsgControl 验证消息的签名。
- 释放内存、关闭 证书存储和释放 证书上下文。
有关如何使用流回调执行这些类似操作的示例,请参阅 示例 C 程序:使用流对消息进行编码和解码。
此示例使用函数 MyHandleError。 此示例中包含此函数的代码。 此函数和其他辅助函数的代码也列在 General_Purpose_Functions下。
//-------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Example of encoding and decoding a signed message.
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <Wincrypt.h>
// 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 CERTIFICATE_STORE_NAME L"MY"
//-------------------------------------------------------------------
// Declare local functions.
//-------------------------------------------------------------------
BOOL EncodeMessage(PCRYPT_DATA_BLOB pEncodedData,
LPWSTR pwszSignerName);
void DecodeMessage(PCRYPT_DATA_BLOB pEncodedData,
LPWSTR pwszSignerName);
// 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.
void ReportFailure()
{
switch (GetLastError())
{
case CRYPT_E_AUTH_ATTR_MISSING:
printf("Message does not contain an expected "
"attribute.\n");
break;
case CRYPT_E_BAD_ENCODE:
printf("An error encountered encoding or decoding.\n");
break;
case CRYPT_E_HASH_VALUE:
printf("The hash value is not correct.\n");
break;
case CRYPT_E_INVALID_MSG_TYPE:
printf("The message type is not valid.\n");
break;
case CRYPT_E_OSS_ERROR:
printf("OSS error.\n");
break;
case CRYPT_E_SIGNER_NOT_FOUND:
printf("Signer not found.\n");
break;
case CRYPT_E_UNEXPECTED_ENCODING:
printf("Unexpected encoding. \n");
break;
case CRYPT_E_UNKNOWN_ALGO:
printf("Unknown algorithm.\n");
break;
case E_OUTOFMEMORY:
printf("Out of memory.\n");
break;
case ERROR_INVALID_HANDLE:
printf("The handle from verify signature is not valid." \
"function.\n");
break;
case ERROR_INVALID_PARAMETER:
printf("The parameter from verify signature "
"is not valid.\n");
break;
case NTE_BAD_FLAGS:
printf("Bad Flags from verify signature function.\n");
break;
case NTE_BAD_HASH:
printf("Bad Hash from verify signature function.\n");
break;
case NTE_BAD_KEY:
printf("Bad Key from verify signature function.\n");
break;
case NTE_BAD_SIGNATURE:
printf("Bad signature from verify signature " \
"function.\n");
break;
case NTE_BAD_UID:
printf("Bad UID from verify signature function.\n");
break;
} // End switch.
} // End ReportFailure.
void EncodeAndDecodeMessage(LPWSTR pwszSignerName)
{
CRYPT_DATA_BLOB EncodedBlob;
if(EncodeMessage(&EncodedBlob, pwszSignerName))
{
DecodeMessage(&EncodedBlob, pwszSignerName);
}
}
BOOL EncodeMessage(PCRYPT_DATA_BLOB pEncodedBlob,
LPWSTR pwszSignerName)
{
/*---------------------------------------------------------------
Declare and initialize variables. This includes getting a
pointer to the message content. This sample creates
the message content and gets a pointer to it. In most
situations, the content will exist somewhere, and a
pointer to it will get passed to the application.
---------------------------------------------------------------*/
HCERTSTORE hSystemStoreHandle;
CRYPT_SIGN_MESSAGE_PARA SignMessagePara;
//---------------------------------------------------------------
// The message to be signed and encoded.
BYTE* pbContent = (BYTE*) "The quick brown fox jumped over " \
"the lazy dog.";
/*---------------------------------------------------------------
The length of the message. This must be one more than the
value returned by strlen() to include the terminal NULL
character.
---------------------------------------------------------------*/
DWORD cbContent = lstrlenA((char *) pbContent) + 1;
//---------------------------------------------------------------
// Arrays to hold the message to be signed and its length.
const BYTE *rgpbToBeSigned[1] ;
DWORD rgcbToBeSigned[1];
//---------------------------------------------------------------
// The signer's certificate.
PCCERT_CONTEXT pSignerCert;
//---------------------------------------------------------------
// Buffer to hold the name of the subject of a certificate.
char pszNameString[MAX_NAME];
//---------------------------------------------------------------
// The following variables are used only in the decoding phase.
DWORD cbData = sizeof(DWORD);
//---------------------------------------------------------------
// Begin processing. Display the original message.
rgpbToBeSigned[0] = pbContent;
rgcbToBeSigned[0] = cbContent;
printf("The original message = \n%s\n\n",
rgpbToBeSigned[0]);
//---------------------------------------------------------------
// Open a certificate store.
if(hSystemStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
CERTIFICATE_STORE_NAME))
{
printf("The certificate store is open. \n");
}
else
{
MyHandleError( "Error Getting Store Handle");
}
/*---------------------------------------------------------------
Find a certificate in the store. This certificate will be
used to sign the message. To sign the message, the
certificate must have a private key accessible.
---------------------------------------------------------------*/
if(pSignerCert = CertFindCertificateInStore(
hSystemStoreHandle,
MY_ENCODING_TYPE,
0,
CERT_FIND_SUBJECT_STR,
pwszSignerName,
NULL))
{
//-----------------------------------------------------------
// 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("Getting the name of the signer " \
"failed.\n");
}
}
else
{
MyHandleError("Signer certificate not found.");
}
/*---------------------------------------------------------------
Initialize the CRYPT_SIGN_MESSAGE_PARA structure. First, use
memset to set all members to zero or NULL. Then set the values of
all members that must be nonzero.
---------------------------------------------------------------*/
memset(&SignMessagePara, 0, sizeof(CRYPT_SIGN_MESSAGE_PARA));
SignMessagePara.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
SignMessagePara.HashAlgorithm.pszObjId = szOID_RSA_MD2;
SignMessagePara.pSigningCert = pSignerCert;
SignMessagePara.dwMsgEncodingType = MY_ENCODING_TYPE;
SignMessagePara.cMsgCert = 1;
SignMessagePara.rgpMsgCert = &pSignerCert;
/*---------------------------------------------------------------
In two steps, sign and encode the message. First, get the
number of bytes required for the buffer to hold the signed
and encoded message.
---------------------------------------------------------------*/
if( CryptSignMessage(
&SignMessagePara,
FALSE,
1,
rgpbToBeSigned,
rgcbToBeSigned,
NULL,
&pEncodedBlob->cbData))
{
printf("The needed length is %d \n", pEncodedBlob->cbData);
}
else
{
MyHandleError("Getting the length failed.\n");
}
//---------------------------------------------------------------
// Allocate memory for the required buffer.
pEncodedBlob->pbData = (BYTE *)malloc(pEncodedBlob->cbData);
if(!pEncodedBlob->pbData)
{
MyHandleError("Memory allocation failed.");
}
//---------------------------------------------------------------
// Call CryptSignMessage a second time to
// copy the signed and encoded message to the buffer.
if( CryptSignMessage(
&SignMessagePara,
FALSE,
1,
rgpbToBeSigned,
rgcbToBeSigned,
pEncodedBlob->pbData,
&pEncodedBlob->cbData))
{
printf("Signing worked \n");
}
else
{
MyHandleError("Signing failed.\n");
}
//---------------------------------------------------------------
// Clean up after signing and encoding.
if(pSignerCert)
{
CertFreeCertificateContext(pSignerCert);
}
if(hSystemStoreHandle)
{
CertCloseStore(hSystemStoreHandle,
CERT_CLOSE_STORE_FORCE_FLAG);
}
return TRUE;
}
void DecodeMessage(PCRYPT_DATA_BLOB pEncodedBlob,
LPWSTR pwszSignerName)
{
//---------------------------------------------------------------
// Buffer to hold the name of the subject of a certificate.
char pszNameString[MAX_NAME];
//---------------------------------------------------------------
// The following variables are used only in the decoding phase.
HCRYPTMSG hMsg;
HCERTSTORE hStoreHandle; // certificate store handle
DWORD cbData = sizeof(DWORD);
DWORD cbDecoded;
BYTE *pbDecoded;
DWORD cbSignerCertInfo;
PCERT_INFO pSignerCertInfo;
PCCERT_CONTEXT pSignerCertContext;
/*---------------------------------------------------------------
The following code decodes the message and verifies the
message signature. This code would normally be in a
stand-alone program that would read the signed and encoded
message and its length from a file from an email message,
or from some other source.
---------------------------------------------------------------*/
//---------------------------------------------------------------
// Open a message for decoding.
if(hMsg = CryptMsgOpenToDecode(
MY_ENCODING_TYPE, // encoding type
0, // flags
0, // use the default message type
// the message type is
// listed in the message header
NULL, // cryptographic provider
// use NULL for the default provider
NULL, // recipient information
NULL)) // stream information
{
printf("The message to decode is open. \n");
}
else
{
MyHandleError("OpenToDecode failed");
}
//---------------------------------------------------------------
// Update the message with an encoded BLOB.
if(CryptMsgUpdate(
hMsg, // handle to the message
pEncodedBlob->pbData, // pointer to the encoded BLOB
pEncodedBlob->cbData, // size of the encoded BLOB
TRUE)) // last call
{
printf("The encoded BLOB has been added to the message. \n");
}
else
{
MyHandleError("Decode MsgUpdate failed");
}
//---------------------------------------------------------------
// Get the number of bytes needed for a buffer
// to hold the decoded message.
if(CryptMsgGetParam(
hMsg, // handle to the message
CMSG_CONTENT_PARAM, // parameter type
0, // index
NULL,
&cbDecoded)) // size of the returned information
{
printf("The message parameter has been acquired. \n");
}
else
{
MyHandleError("Decode CMSG_CONTENT_PARAM failed.");
}
//---------------------------------------------------------------
// Allocate memory.
if(!(pbDecoded = (BYTE *) malloc(cbDecoded)))
{
MyHandleError("Decode memory allocation failed.");
}
//---------------------------------------------------------------
// Copy the content to the buffer.
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 decoded message is =>\n%s\n\n",
(LPSTR)pbDecoded);
}
else
{
MyHandleError("Decode CMSG_CONTENT_PARAM #2 failed");
}
//---------------------------------------------------------------
// Verify the signature.
// First, get the signer CERT_INFO from the message.
//---------------------------------------------------------------
// Get the size of memory required for the certificate.
if(CryptMsgGetParam(
hMsg, // handle to the message
CMSG_SIGNER_CERT_INFO_PARAM, // parameter type
0, // index
NULL,
&cbSignerCertInfo)) // size of the returned
// information
{
printf("%d bytes needed for the buffer.\n",
cbSignerCertInfo);
}
else
{
MyHandleError("Verify SIGNER_CERT_INFO #1 failed.");
}
//---------------------------------------------------------------
// Allocate memory.
if(!(pSignerCertInfo = (PCERT_INFO) malloc(cbSignerCertInfo)))
{
MyHandleError("Verify memory allocation failed.");
}
//---------------------------------------------------------------
// Get the message certificate information (CERT_INFO
// structure).
if(!(CryptMsgGetParam(
hMsg, // handle to the message
CMSG_SIGNER_CERT_INFO_PARAM, // parameter type
0, // index
pSignerCertInfo, // address for returned
// information
&cbSignerCertInfo))) // size of the returned
// information
{
MyHandleError("Verify SIGNER_CERT_INFO #2 failed");
}
//---------------------------------------------------------------
// Open a certificate store in memory using CERT_STORE_PROV_MSG,
// which initializes it with the certificates from the message.
if(hStoreHandle = CertOpenStore(
CERT_STORE_PROV_MSG, // store provider type
MY_ENCODING_TYPE, // encoding type
NULL, // cryptographic provider
// use NULL for the default
0, // flags
hMsg)) // handle to the message
{
printf("The certificate store to be used for message " \
"verification has been opened.\n");
}
else
{
MyHandleError("Verify open store failed");
}
//---------------------------------------------------------------
// Find the signer's certificate in the store.
if(pSignerCertContext = CertGetSubjectCertificateFromStore(
hStoreHandle, // handle to the store
MY_ENCODING_TYPE, // encoding type
pSignerCertInfo)) // pointer to retrieved CERT_CONTEXT
{
if(CertGetNameString(
pSignerCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
pszNameString,
MAX_NAME) > 1)
{
printf("The message signer is %s \n",pszNameString);
}
else
{
MyHandleError("Getting the signer's name failed.\n");
}
}
else
{
MyHandleError("Verify GetSubjectCert failed");
}
//---------------------------------------------------------------
// Use the CERT_INFO from the signer certificate to verify
// the signature.
if(CryptMsgControl(
hMsg,
0,
CMSG_CTRL_VERIFY_SIGNATURE,
pSignerCertContext->pCertInfo))
{
printf("Verify signature succeeded. \n");
}
else
{
printf("The signature was not verified. \n");
ReportFailure();
}
//---------------------------------------------------------------
// Clean up.
if(pEncodedBlob->pbData)
{
free(pEncodedBlob->pbData);
pEncodedBlob->pbData = NULL;
}
if(pbDecoded)
{
free(pbDecoded);
}
if(pSignerCertInfo)
{
free(pSignerCertInfo);
}
if(pSignerCertContext)
{
CertFreeCertificateContext(pSignerCertContext);
}
if(hStoreHandle)
{
CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
}
if(hMsg)
{
CryptMsgClose(hMsg);
}
}