Programma C di esempio: firma, codifica, decodifica e verifica di un messaggio
Nell'esempio seguente viene combinata la firma e la codifica di un messaggio e la decodifica di un messaggio firmato e la verifica della firma. Le due operazioni in genere si trovano in programmi separati. L'esempio di codifica crea il messaggio codificato, lo salva in un file su disco o in altro modo lo invia a un altro utente. L'esempio di decodifica riceve il messaggio codificato, lo decodifica e verifica la firma. I due processi sono stati combinati qui per mostrare il funzionamento di entrambe le procedure.
La firma e la codifica di un messaggio non garantisce la privacy per tale messaggio. Piuttosto garantisce l'autenticità del messaggio. Poiché il messaggio viene firmato con la chiave privata del mittente, quando il destinatario del messaggio decrittografa la firma con la chiave pubblica del mittente (disponibile dal certificato inviato insieme al messaggio), il destinatario può assicurarsi che il messaggio sia stato inviato dalla persona o dall'entità associata al certificato e che il messaggio non sia stato modificato dopo la firma.
Questo esempio illustra le attività e le funzioni CryptoAPI seguenti per la codifica di un messaggio:
- Apertura di un archivio certificati tramite CertOpenStore.
- Recupero di un certificato con un nome soggetto specifico usando CertFindCertificateInStore.
- Ottenere e stampare il nome del soggetto di un certificato usando CertGetNameString.
- Inizializzazione di una struttura CRYPT_SIGN_MESSAGE_PARA da usare in una chiamata a CryptSignMessage.
- Firma e codifica di un messaggio con CryptSignMessage.
Questo esempio illustra le attività e le funzioni CryptoAPI seguenti per decodificare un messaggio e verificare la firma:
- Apertura di un messaggio per la decodifica con CryptMsgOpenToDecode.
- Aggiunta del BLOB codificato al messaggio da decodificare usando CryptMsgUpdate.
- Decodifica del messaggio tramite CryptMsgGetParam.
- Apertura di un archivio certificati in memoria con CertOpenStore usando il messaggio ricevuto e decodificato.
- Uso di CertGetSubjectCertificateFromStore per ottenere il certificato del firmatario del messaggio.
- Verifica della firma di un messaggio tramite CryptMsgControl.
- Liberare memoria, chiudere gli archivi certificati e liberare il contesto del certificato.
Per un esempio di come eseguire queste operazioni simili usando un callback di flusso, vedere Esempio di programma C: codifica e decodifica di un messaggio tramite un flusso.
In questo esempio viene usata la funzione MyHandleError. Il codice per questa funzione è incluso nell'esempio. Il codice per questa e altre funzioni ausiliarie è elencato anche in 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);
}
}