Exemplo de programa C: cosigning e decodificação de uma mensagem
Você pode usar a função CryptSignMessage para cosign uma mensagem. Isso é feito chamando CryptSignMessage uma vez para assinar a mensagem original e, em seguida, chame CryptSignMessage novamente para cosign a mensagem assinada.
Ao verificar a assinatura de uma mensagem cosignada, você usa a função CryptGetMessageSignerCount para obter o número de signatários da mensagem e, em seguida, chamar o CryptVerifyMessageSignature para cada assinatura. Se todas as assinaturas forem verificadas, você saberá que a mensagem cosignada é válida.
O exemplo a seguir mostra como assinar uma mensagem por mais de uma pessoa (cotribuir a mensagem), verificar todas as assinaturas e decodificar a mensagem.
Observação
Essa função pode retornar uma contagem de signatários duplicados e, portanto, pode não ser suficiente para evitar ataques. É recomendável usar o campo sid (SignerIdentifier) do SignerInfo para identificar os signatários duplicados em uma mensagem.
//-------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
#pragma comment(lib, "crypt32.lib")
#include <stdio.h>
#include <conio.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 the name of the store where the needed certificate can be
// found.
#define CERT_STORE_NAME L"MY"
//-------------------------------------------------------------------
// Define the name of a certificate subject.
// To use this program, the definitions of SIGNER_NAME and
// CO_SIGNER_NAME must be changed to the name of the subject of a
// certificate that has access to a private key. That certificate
// must have either the CERT_KEY_PROV_INFO_PROP_ID or
// CERT_KEY_CONTEXT_PROP_ID property set for the context to
// provide access to the private signature key.
//-------------------------------------------------------------------
// You can use commands similar to the following to create a
// certificates that can be used with this example:
//
// makecert -n "cn=test_signer" -sk Test -ss my
// makecert -n "cn=test_co_signer" -sk Test -ss my
//#define SIGNER_NAME L"test_signer"
//#define CO_SIGNER_NAME L"test_co_signer"
#define SIGNER_NAME L"Insert_signer_name_here"
#define CO_SIGNER_NAME L"Insert_co_signer_name_here"
#define MAX_NAME 256
//-------------------------------------------------------------------
// Local function prototypes.
void MyHandleError(LPTSTR psz);
bool SignMessage(CRYPT_DATA_BLOB *pEncodedMessageBlob);
bool CosignMessage(CRYPT_DATA_BLOB *pSignedMessageBlob,
CRYPT_DATA_BLOB *pCosignedMessageBlob);
bool VerifyCosignedMessage(CRYPT_DATA_BLOB *pEncodedMessageBlob,
CRYPT_DATA_BLOB *pDecodedMessageBlob);
int _tmain(int argc, _TCHAR* argv[])
{
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
CRYPT_DATA_BLOB EncodedMessage;
if(SignMessage(&EncodedMessage))
{
CRYPT_DATA_BLOB DecodedMessage;
if(VerifyCosignedMessage(&EncodedMessage, &DecodedMessage))
{
free(DecodedMessage.pbData);
}
free(EncodedMessage.pbData);
}
_tprintf(TEXT("Press any key to exit."));
_getch();
return 0;
}
//-------------------------------------------------------------------
// 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"));
} // End of MyHandleError.
//-------------------------------------------------------------------
// SignMessage
bool SignMessage(CRYPT_DATA_BLOB *pEncodedMessageBlob)
{
bool fReturn = false;
BYTE* pbMessage;
DWORD cbMessage;
HCERTSTORE hCertStore = NULL;
PCCERT_CONTEXT pSignerCert = NULL;
CRYPT_SIGN_MESSAGE_PARA SigParams;
DWORD cbSignedMessageBlob=0;
BYTE *pbSignedMessageBlob = NULL;
// Initialize the output pointer.
pEncodedMessageBlob->cbData = 0;
pEncodedMessageBlob->pbData = NULL;
// The message to be signed.
// Usually, the message exists somewhere and a pointer is
// passed to the application.
pbMessage =
(BYTE*)TEXT("CryptoAPI is a good way to handle security");
// Calculate the size of message. To include the
// terminating null character, the length is one more byte
// than the length returned by the strlen function.
cbMessage = (lstrlen((TCHAR*) pbMessage) + 1) * sizeof(TCHAR);
// Create the MessageArray and the MessageSizeArray.
const BYTE* MessageArray[] = {pbMessage};
DWORD MessageSizeArray[1];
MessageSizeArray[0] = cbMessage;
// Begin processing.
_tprintf(TEXT("The message to be signed is \"%s\".\n"),
pbMessage);
// Open the certificate store.
if(!(hCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
CERT_STORE_NAME)))
{
MyHandleError(TEXT("The MY store could not be opened."));
goto exit_SignMessage;
}
// Get a pointer to the signer's certificate.
// This certificate must have access to the signer's private key.
if(pSignerCert = CertFindCertificateInStore(
hCertStore,
MY_ENCODING_TYPE,
0,
CERT_FIND_SUBJECT_STR,
SIGNER_NAME,
NULL))
{
_tprintf(TEXT("The signer's certificate was found.\n"));
}
else
{
MyHandleError( TEXT("Signer certificate not found."));
goto exit_SignMessage;
}
// Initialize the signature structure.
SigParams.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
SigParams.dwMsgEncodingType = MY_ENCODING_TYPE;
SigParams.pSigningCert = pSignerCert;
SigParams.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
SigParams.HashAlgorithm.Parameters.cbData = NULL;
SigParams.cMsgCert = 1;
SigParams.rgpMsgCert = &pSignerCert;
SigParams.cAuthAttr = 0;
SigParams.dwInnerContentType = 0;
SigParams.cMsgCrl = 0;
SigParams.cUnauthAttr = 0;
SigParams.dwFlags = 0;
SigParams.pvHashAuxInfo = NULL;
SigParams.rgAuthAttr = NULL;
// First, get the size of the signed BLOB.
if(CryptSignMessage(
&SigParams,
FALSE,
1,
MessageArray,
MessageSizeArray,
NULL,
&cbSignedMessageBlob))
{
_tprintf(TEXT("%d bytes needed for the encoded BLOB.\n"),
cbSignedMessageBlob);
}
else
{
MyHandleError(TEXT("Getting signed BLOB size failed"));
goto exit_SignMessage;
}
// Allocate memory for the signed BLOB.
if(!(pbSignedMessageBlob =
(BYTE*)malloc(cbSignedMessageBlob)))
{
MyHandleError(
TEXT("Memory allocation error while signing."));
goto exit_SignMessage;
}
// Get the signed message BLOB.
if(CryptSignMessage(
&SigParams,
FALSE,
1,
MessageArray,
MessageSizeArray,
pbSignedMessageBlob,
&cbSignedMessageBlob))
{
_tprintf(TEXT("The message was signed successfully. \n"));
// pbSignedMessageBlob now contains the signed BLOB.
fReturn = true;
}
else
{
MyHandleError(TEXT("Error getting signed BLOB"));
goto exit_SignMessage;
}
exit_SignMessage:
// Clean up and free memory as needed.
if(pSignerCert)
{
CertFreeCertificateContext(pSignerCert);
pSignerCert = NULL;
}
if(hCertStore)
{
CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG);
hCertStore = NULL;
}
if(pbSignedMessageBlob && fReturn)
{
fReturn = false;
CRYPT_DATA_BLOB SignedMessageBlob;
CRYPT_DATA_BLOB CosignedMessageBlob;
SignedMessageBlob.cbData = cbSignedMessageBlob;
SignedMessageBlob.pbData = pbSignedMessageBlob;
if(CosignMessage(&SignedMessageBlob, &CosignedMessageBlob))
{
pEncodedMessageBlob->cbData = CosignedMessageBlob.cbData;
pEncodedMessageBlob->pbData = CosignedMessageBlob.pbData;
fReturn = true;
}
}
if(pbSignedMessageBlob)
{
free(pbSignedMessageBlob);
pbSignedMessageBlob = NULL;
}
return fReturn;
}
//-------------------------------------------------------------------
// CosignMessage
bool CosignMessage(
CRYPT_DATA_BLOB *pSignedMessageBlob,
CRYPT_DATA_BLOB *pCosignedMessageBlob)
{
bool fReturn = false;
HCERTSTORE hCertStore = NULL;
PCCERT_CONTEXT pCosignerCert = NULL;
HCRYPTPROV hCryptProv = NULL;
HCRYPTMSG hMsg = NULL;
DWORD cbCosignedMessageBlob=0;
BYTE *pbCosignedMessageBlob = NULL;
// Initialize the output pointer.
pCosignedMessageBlob->cbData = 0;
pCosignedMessageBlob->pbData = NULL;
// Open the certificate store.
if(!(hCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
CERT_STORE_NAME)))
{
MyHandleError(TEXT("The MY store could not be opened."));
goto exit_CosignMessage;
}
// Get a pointer to the cosigner's certificate.
// This certificate must have access to the cosigner's private
// key.
if((pCosignerCert = CertFindCertificateInStore(
hCertStore,
MY_ENCODING_TYPE,
0,
CERT_FIND_SUBJECT_STR,
CO_SIGNER_NAME,
NULL)))
{
_tprintf(TEXT("The signer's certificate was found.\n"));
}
else
{
MyHandleError( TEXT("Signer certificate not found."));
goto exit_CosignMessage;
}
DWORD dwKeySpec;
if(!(CryptAcquireCertificatePrivateKey(
pCosignerCert,
0,
NULL,
&hCryptProv,
&dwKeySpec,
NULL)))
{
MyHandleError(
TEXT("CryptAcquireCertificatePrivateKey failed."));
goto exit_CosignMessage;
}
// Open a message for decoding.
if(!(hMsg = CryptMsgOpenToDecode(
MY_ENCODING_TYPE,
0,
0,
NULL,
NULL,
NULL)))
{
MyHandleError(TEXT("CryptMsgOpenToDecode failed."));
goto exit_CosignMessage;
}
// Update the message with the encoded BLOB.
if(!(CryptMsgUpdate(
hMsg,
pSignedMessageBlob->pbData,
pSignedMessageBlob->cbData,
TRUE)))
{
MyHandleError(TEXT("CryptMsgUpdate failed."));
goto exit_CosignMessage;
}
// Initialize the CMSG_SIGNER_ENCODE_INFO structure for the
// cosigner.
CMSG_SIGNER_ENCODE_INFO CosignerInfo;
memset(&CosignerInfo, 0, sizeof(CMSG_SIGNER_ENCODE_INFO));
CosignerInfo.cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
CosignerInfo.pCertInfo = pCosignerCert->pCertInfo;
CosignerInfo.hCryptProv = hCryptProv;
CosignerInfo.dwKeySpec = dwKeySpec;
CosignerInfo.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
// Add the cosigner to the message.
if(CryptMsgControl(
hMsg,
0,
CMSG_CTRL_ADD_SIGNER,
&CosignerInfo))
{
_tprintf(TEXT("CMSG_CTRL_ADD_SIGNER succeeded. \n"));
}
else
{
MyHandleError(TEXT("CMSG_CTRL_ADD_SIGNER failed."));
goto exit_CosignMessage;
}
// Add the cosigner's certificate to the message.
CERT_BLOB CosignCertBlob;
CosignCertBlob.cbData = pCosignerCert->cbCertEncoded;
CosignCertBlob.pbData = pCosignerCert->pbCertEncoded;
if(CryptMsgControl(
hMsg,
0,
CMSG_CTRL_ADD_CERT,
&CosignCertBlob))
{
_tprintf(TEXT("CMSG_CTRL_ADD_CERT succeeded. \n"));
}
else
{
MyHandleError(TEXT("CMSG_CTRL_ADD_CERT failed."));
goto exit_CosignMessage;
}
// Get the size of the cosigned BLOB.
if(CryptMsgGetParam(
hMsg,
CMSG_ENCODED_MESSAGE,
0,
NULL,
&cbCosignedMessageBlob))
{
_tprintf(TEXT("The size for the encoded BLOB is %d.\n"),
cbCosignedMessageBlob);
}
else
{
MyHandleError(TEXT("Sizing of cbSignerInfo failed."));
goto exit_CosignMessage;
}
// Allocate memory for the cosigned BLOB.
if(!(pbCosignedMessageBlob =
(BYTE*)malloc(cbCosignedMessageBlob)))
{
MyHandleError(
TEXT("Memory allocation error while cosigning."));
goto exit_CosignMessage;
}
// Get the cosigned message BLOB.
if(CryptMsgGetParam(
hMsg,
CMSG_ENCODED_MESSAGE,
0,
pbCosignedMessageBlob,
&cbCosignedMessageBlob))
{
_tprintf(TEXT("The message was cosigned successfully. \n"));
// pbSignedMessageBlob now contains the signed BLOB.
fReturn = true;
}
else
{
MyHandleError(TEXT("Sizing of cbSignerInfo failed."));
goto exit_CosignMessage;
}
exit_CosignMessage:
// Clean up and free memory as needed.
if(hMsg)
{
CryptMsgClose(hMsg);
}
if(hCryptProv)
{
CryptReleaseContext(hCryptProv, 0);
hCryptProv = NULL;
}
if(pCosignerCert)
{
CertFreeCertificateContext(pCosignerCert);
pCosignerCert = NULL;
}
if(hCertStore)
{
CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG);
hCertStore = NULL;
}
// Only free the cosigned message if a failure occurred.
if(!fReturn)
{
if(pbCosignedMessageBlob)
{
free(pbCosignedMessageBlob);
pbCosignedMessageBlob = NULL;
}
}
if(pbCosignedMessageBlob)
{
pCosignedMessageBlob->cbData = cbCosignedMessageBlob;
pCosignedMessageBlob->pbData = pbCosignedMessageBlob;
}
return fReturn;
}
//-------------------------------------------------------------------
// VerifyCosignedMessage
bool VerifyCosignedMessage(
CRYPT_DATA_BLOB *pCosignedMessageBlob,
CRYPT_DATA_BLOB *pDecodedMessageBlob)
{
bool fReturn = false;
BYTE *pbDecodedMessage = NULL;
DWORD cbDecodedMessage=0;
// Get the number of signers of the message.
LONG lSigners = CryptGetMessageSignerCount(
MY_ENCODING_TYPE,
pCosignedMessageBlob->pbData,
pCosignedMessageBlob->cbData);
if(-1 == lSigners)
{
MyHandleError(TEXT("CryptGetMessageSignerCount failed."));
goto exit_VerifyCosignedMessage;
}
// Loop through all of the signers and verify the signature for
// each one.
CRYPT_VERIFY_MESSAGE_PARA VerifyParams;
VerifyParams.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
VerifyParams.dwMsgAndCertEncodingType = MY_ENCODING_TYPE;
VerifyParams.hCryptProv = NULL;
VerifyParams.pfnGetSignerCertificate = NULL;
VerifyParams.pvGetArg = NULL;
for(LONG i = 0; i < lSigners; i++)
{
if(!(CryptVerifyMessageSignature(
&VerifyParams,
i,
pCosignedMessageBlob->pbData,
pCosignedMessageBlob->cbData,
NULL,
NULL,
NULL)))
{
MyHandleError(TEXT("One of the message signatures ")
TEXT("could not be verified."));
goto exit_VerifyCosignedMessage;
}
}
// At this point, all of the signatures in the message have been
// verified. Get the decoded data from the message.
_tprintf(
TEXT("All signatures in the message have been verified.\n"));
// Get the size of the decoded message
if(!(CryptVerifyMessageSignature(
&VerifyParams,
0,
pCosignedMessageBlob->pbData,
pCosignedMessageBlob->cbData,
NULL,
&cbDecodedMessage,
NULL)))
{
MyHandleError(TEXT("CryptVerifyMessageSignature failed."));
goto exit_VerifyCosignedMessage;
}
// Allocate memory for the decoded message.
if(!(pbDecodedMessage =
(BYTE*)malloc(cbDecodedMessage)))
{
MyHandleError(
TEXT("Memory allocation error while decoding."));
goto exit_VerifyCosignedMessage;
}
if((CryptVerifyMessageSignature(
&VerifyParams,
0,
pCosignedMessageBlob->pbData,
pCosignedMessageBlob->cbData,
pbDecodedMessage,
&cbDecodedMessage,
NULL)))
{
fReturn = true;
}
else
{
MyHandleError(TEXT("CryptVerifyMessageSignature failed."));
goto exit_VerifyCosignedMessage;
}
_tprintf(TEXT("The verified message is \"%s\".\n"),
pbDecodedMessage);
exit_VerifyCosignedMessage:
// If an error occurred and memory was allocated, free it.
if(!fReturn)
{
if(pbDecodedMessage)
{
free(pbDecodedMessage);
pbDecodedMessage = NULL;
}
}
if(pbDecodedMessage)
{
pDecodedMessageBlob->cbData = cbDecodedMessage;
pDecodedMessageBlob->pbData = pbDecodedMessage;
}
return fReturn;
}