Contoh Program C: Menandatangani, Mengodekan, Mendekode, dan Memverifikasi Pesan
Contoh berikut menggabungkan penandatanganan dan pengodean pesan, dan mendekode pesan yang ditandatangani dan memverifikasi tanda tangan. Kedua operasi biasanya akan berada dalam program terpisah. Contoh pengodean akan membuat pesan yang dikodekan, menyimpannya ke file disk atau dengan cara lain mengirimkannya ke pengguna lain. Contoh pendekodean akan menerima pesan yang dikodekan, mendekodenya, dan memverifikasi tanda tangan. Kedua proses telah digabungkan di sini untuk menunjukkan kedua prosedur yang berfungsi.
Menandatangani dan mengodekan pesan tidak memastikan privasi untuk pesan tersebut. Sebaliknya memastikan keaslian pesan. Karena pesan ditandatangani dengan kunci privat pengirim, ketika penerima pesan mendekripsi tanda tangan dengan kunci umum pengirim (tersedia dari sertifikat yang dikirim bersama dengan pesan), penerima dapat memastikan bahwa pesan dikirim oleh orang atau entitas yang terkait dengan sertifikat dan bahwa pesan tidak diubah setelah ditandatangani.
Contoh ini mengilustrasikan tugas-tugas berikut dan fungsi CryptoAPI untuk mengodekan pesan:
- Membuka penyimpanan sertifikat menggunakan CertOpenStore.
- Mengambil sertifikat dengan nama subjek tertentu menggunakan CertFindCertificateInStore.
- Mendapatkan dan mencetak nama subjek sertifikat menggunakan CertGetNameString.
- Menginisialisasi struktur CRYPT_SIGN_MESSAGE_PARA yang akan digunakan dalam panggilan ke CryptSignMessage.
- Menandatangani dan mengodekan pesan dengan CryptSignMessage.
Contoh ini mengilustrasikan tugas dan fungsi CryptoAPI berikut untuk mendekode pesan dan memverifikasi tanda tangan:
- Membuka pesan untuk pendekodean dengan CryptMsgOpenToDecode.
- Menambahkan BLOB yang dikodekan ke pesan yang akan didekodekan dengan menggunakan CryptMsgUpdate.
- Mendekode pesan menggunakan CryptMsgGetParam.
- Membuka penyimpanan sertifikat dalam memori dengan CertOpenStore menggunakan pesan yang diterima dan didekodekan.
- Menggunakan CertGetSubjectCertificateFromStore untuk mendapatkan sertifikat penanda tangan pesan.
- Memverifikasi tanda tangan pesan menggunakan CryptMsgControl.
- Membebaskan memori, menutup penyimpanan sertifikat, dan membebaskan konteks sertifikat.
Untuk contoh cara melakukan operasi serupa ini menggunakan panggilan balik aliran, lihat Contoh Program C: Pengodean dan Pendekodean Pesan Menggunakan Stream.
Contoh ini menggunakan fungsi MyHandleError. Kode untuk fungsi ini disertakan dengan sampel. Kode untuk fungsi tambahan ini dan lainnya juga tercantum di bawah 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);
}
}