範例 C 程式:共同簽署和解碼訊息

您可以使用 CryptSignMessage 函式來共同簽署訊息。 這可藉由呼叫 CryptSignMessage 一次來簽署原始訊息來完成,然後再次呼叫 CryptSignMessage 以共同簽署已簽署的訊息。

當您驗證共同簽署訊息的簽章時,您會使用 CryptGetMessageSignerCount 函式來取得訊息的簽署者數目,然後針對每個簽章呼叫 CryptVerifyMessageSignature 。 如果已驗證所有簽章,則您知道已簽署的訊息有效。

下列範例示範如何透過一個以上的人員簽署訊息, (將訊息) 、驗證所有簽章,以及解碼訊息。

注意

此函式可能會傳回重複的簽署者計數,因此可能不足以避免攻擊。 建議您從 SignerInfo 使用 sid (SignerIdentifier) 欄位來識別訊息中的重複簽署者。

//-------------------------------------------------------------------
// 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;
}