Пример программы C: подписывание, кодировка, декодирование и проверка сообщения
В следующем примере объединяются подписывание и кодировка сообщения, а также декодирование подписанного сообщения и проверка подписи. Эти две операции обычно выполняются в разных программах. Пример кодирования создает закодированное сообщение, сохраняет его в файл на диске или каким-либо другим способом отправляет его другому пользователю. Пример декодирования получает закодированное сообщение, декодирует его и проверяет подпись. Здесь два процесса были объединены, чтобы показать, что обе процедуры работают.
Подписывание и кодирование сообщения не гарантирует конфиденциальность этого сообщения. Скорее, он обеспечивает подлинность сообщения. Поскольку сообщение подписывается закрытым ключом отправителя, когда получатель сообщения расшифровывает подпись с помощью открытого ключа отправителя (доступного из сертификата, отправляемого вместе с сообщением), получатель может быть уверен, что сообщение было отправлено лицом или сущностью, связанной с сертификатом, и что сообщение не было изменено после его подписания.
В этом примере показаны следующие задачи и функции CryptoAPI для кодирования сообщения:
- Открытие хранилища сертификатов с помощью CertOpenStore.
- Получение сертификата с определенным именем субъекта с помощью CertFindCertificateInStore.
- Получение и печать имени субъекта сертификата с помощью CertGetNameString.
- Инициализация структуры CRYPT_SIGN_MESSAGE_PARA для использования в вызове CryptSignMessage.
- Подписывание и кодирование сообщения с помощью CryptSignMessage.
В этом примере показаны следующие задачи и функции CryptoAPI для декодирования сообщения и проверки подписи:
- Открытие сообщения для декодирования с помощью CryptMsgOpenToDecode.
- Добавление закодированного большого двоичного объекта в сообщение, которое будет декодироваться с помощью CryptMsgUpdate.
- Декодирование сообщения с помощью 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);
}
}