How to sign and verify with CryptoAPI and a user certificate

Hi, welcome back,

Today I will show how to sign data and verify that signature using CryptoAPI and a certificate from our Personal store. The comments in the code should help to understand it. Remember that this is just a sample, and some more cleanup would be needed in case of error, for instance.

<SAMPLE>

 // Includes
#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include <wincrypt.h>

// Defines
#define CERT_PERSONAL_STORE_NAME  L"My"
#define CERT_OTHER_PEOPLE_STORE_NAME L"AddressBook"
#define MY_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define BUFSIZE 1024

// Local functions
void Sign(wchar_t * SignerName, wchar_t * DataFileName, wchar_t * SignatureFileName);
void Verify(wchar_t * SignerName, wchar_t * SignatureFileName, wchar_t * DataFileName);

// ShowUsageAndExit
void ShowUsageAndExit()
{
  wprintf(L"Usage:\n");
   wprintf(L"   - To sign:   SignVerify s signer_name data_file signature_file\n");
    wprintf(L"   - To verify: SignVerify v signer_name data_file signature_file\n");
    wprintf(L"\n<< Press any key to continue >>\n");
    _getch();
   exit(1);

} 
// End of ShowUsageAndExit

// CheckError
void CheckError(BOOL condition, wchar_t * message)
{
    wprintf(message);
   if (condition)                                          
    {                                                   
        wprintf(L"SUCCESS\n");                          
    }                                                   
    else                                                
    {           
        // TODO: Some cleanup
       wprintf(L"FAILURE (0x%x)\n", GetLastError());
       wprintf(L"\n<< Press any key to continue >>\n");
        _getch();
       exit(1);
    }
}
// End CheckError

// Main
void wmain(int argc, wchar_t * argv[])
{

   // Usage
    if (argc != 5) {
        ShowUsageAndExit();
 }

   if (!wcscmp(argv[1], L"s"))
 {
       // Sign
     Sign(argv[2], argv[3], argv[4]);
    } 
  else if (!wcscmp(argv[1], L"v"))
    {
       // Verify
       Verify(argv[2], argv[3], argv[4]);
  } 
  else 
   {
       // Error
        ShowUsageAndExit();
 }

   // The end
  wprintf(L"\n<< Press any key to continue >>\n");
    _getch();

} 
// End of main

// Sign
void Sign(wchar_t * SignerName, wchar_t * DataFileName, wchar_t * SignatureFileName)
{
   // Variables
    HCERTSTORE hStoreHandle = NULL;
 PCCERT_CONTEXT pSignerCert = NULL;
  HCRYPTPROV hCryptProv = NULL;
   DWORD dwKeySpec = 0;
    HCRYPTHASH hHash = NULL;
    HANDLE hDataFile = NULL; 
   BOOL bResult = FALSE;
   BYTE rgbFile[BUFSIZE];
  DWORD cbRead = 0;
   DWORD dwSigLen = 0;
 BYTE * pbSignature = NULL;
  HANDLE hSignatureFile = NULL;
   DWORD lpNumberOfBytesWritten = 0;

   wprintf(L"SIGNING\n\n");

    // Open the certificate store.
  hStoreHandle = CertOpenStore(
       CERT_STORE_PROV_SYSTEM,
     0,
      NULL,
       CERT_SYSTEM_STORE_CURRENT_USER,
     CERT_PERSONAL_STORE_NAME
    );
  CheckError((BOOL)hStoreHandle, L"CertOpenStore....................... ");

   // Get signer's certificate with access to private key.
 do {
        // Get a certificate that matches the search criteria
       pSignerCert = CertFindCertificateInStore(
           hStoreHandle,
           MY_TYPE,
            0,
          CERT_FIND_SUBJECT_STR,
          SignerName,
         pSignerCert
     );
      CheckError((BOOL)pSignerCert, L"CertFindCertificateInStore.......... ");

        // Get the CSP, and check if we can sign with the private key           
        bResult = CryptAcquireCertificatePrivateKey(
            pSignerCert,
            0,
          NULL,
           &hCryptProv,
            &dwKeySpec,
         NULL
        );
      CheckError(bResult, L"CryptAcquireCertificatePrivateKey... ");

  } while ((dwKeySpec & AT_SIGNATURE) != AT_SIGNATURE);

   // Create the hash object.
  bResult = CryptCreateHash(
      hCryptProv, 
        CALG_MD5, 
      0, 
     0, 
     &hHash
  );
  CheckError(bResult, L"CryptCreateHash..................... ");

  // Open the file with the content to be signed 
 hDataFile = CreateFileW(DataFileName,
       GENERIC_READ,
       FILE_SHARE_READ,
        NULL,
       OPEN_EXISTING,
      FILE_FLAG_SEQUENTIAL_SCAN,
      NULL
    );
  CheckError((hDataFile != INVALID_HANDLE_VALUE), L"CreateFile.......................... ");

  // Compute the cryptographic hash of the data.
  while (bResult = ReadFile(hDataFile, rgbFile, BUFSIZE, &cbRead, NULL))
  {
       if (cbRead == 0)
        {
           break;
      }
       CheckError(bResult, L"ReadFile............................ ");

      bResult = CryptHashData(
            hHash, 
         rgbFile, 
           cbRead, 
            0
       );
      CheckError(bResult, L"CryptHashData....................... ");

  }
   CheckError(bResult, L"ReadFile............................ ");

  // Sign the hash object
 dwSigLen = 0;
   bResult = CryptSignHash(
        hHash, 
     AT_SIGNATURE, 
      NULL, 
      0, 
     NULL, 
      &dwSigLen
   );
  CheckError(bResult, L"CryptSignHash....................... ");

  pbSignature = (BYTE *)malloc(dwSigLen);
 CheckError((BOOL)pbSignature, L"malloc.............................. ");

    bResult = CryptSignHash(
        hHash, 
     AT_SIGNATURE, 
      NULL, 
      0, 
     pbSignature, 
       &dwSigLen
   );
  CheckError(bResult, L"CryptSignHash....................... ");

  // Create a file to save the signature
  hSignatureFile = CreateFileW(
       SignatureFileName,
      GENERIC_WRITE,
      0,
      NULL,
       CREATE_ALWAYS,
      FILE_ATTRIBUTE_NORMAL,
      NULL
    );
  CheckError((hSignatureFile != INVALID_HANDLE_VALUE), L"CreateFile.......................... ");

 // Write the signature to the file
  bResult = WriteFile(
        hSignatureFile, 
        (LPCVOID)pbSignature, 
      dwSigLen, 
      &lpNumberOfBytesWritten, 
       NULL
    );
  CheckError(bResult, L"WriteFile........................... ");

  // Clean up and free memory.
    free(pbSignature);

  CloseHandle(hDataFile);
 CloseHandle(hSignatureFile);

    bResult = CryptDestroyHash(hHash);
  CheckError(bResult, L"CryptDestroyHash.................... ");

  bResult = CertFreeCertificateContext(pSignerCert);
  CheckError(bResult, L"CertFreeCertificateContext.......... ");

  bResult = CertCloseStore(
       hStoreHandle, 
      CERT_CLOSE_STORE_CHECK_FLAG
 );
  CheckError(bResult, L"CertCloseStore...................... ");

} 
// End of Sign

// Verify
void Verify(wchar_t * SignerName, wchar_t * DataFileName, wchar_t * SignatureFileName)
{
  // Variables
    HCERTSTORE hStoreHandle = NULL;
 PCCERT_CONTEXT pSignerCert = NULL;
  DWORD dwKeySpec = 0;
    HCRYPTPROV hCryptProv = NULL;
    HCRYPTHASH hHash = NULL;
   HANDLE hDataFile = NULL;
    BOOL bResult = FALSE;
   BYTE rgbFile[BUFSIZE];
  DWORD cbRead = 0;
   HANDLE hSignatureFile = NULL;
   BYTE * pbBinary = NULL;
 DWORD cbBinary = 0;
 HCRYPTKEY hPubKey = NULL;

   wprintf(L"VERIFYING\n\n");

  // Open the certificate store.
  hStoreHandle = CertOpenStore(
       CERT_STORE_PROV_SYSTEM,
     0,
      NULL,
       CERT_SYSTEM_STORE_CURRENT_USER,
     CERT_PERSONAL_STORE_NAME    
    );
  CheckError((BOOL)hStoreHandle, L"CertOpenStore....................... ");

   // Get a certificate that matches the search criteria
   pSignerCert = CertFindCertificateInStore(
       hStoreHandle,
       MY_TYPE,
        0,
      CERT_FIND_SUBJECT_STR,
      SignerName,
     pSignerCert
 );
  CheckError((BOOL)pSignerCert, L"CertFindCertificateInStore.......... ");

    // Get the CSP
  bResult = CryptAcquireContext(
      &hCryptProv,
        NULL,
       NULL,
       PROV_RSA_FULL,
      CRYPT_VERIFYCONTEXT
 );
  CheckError(bResult, L"CryptAcquireContext................. ");

  // Create the hash object.
  bResult = CryptCreateHash(
      hCryptProv, 
        CALG_MD5, 
      0, 
     0, 
     &hHash
  );
  CheckError(bResult, L"CryptCreateHash..................... ");

  // Open the file with the content that was signed.
  hDataFile = CreateFileW(
        DataFileName,
       GENERIC_READ,
       FILE_SHARE_READ,
        NULL,
       OPEN_EXISTING,
      FILE_FLAG_SEQUENTIAL_SCAN,
      NULL
    );
  CheckError((hDataFile != INVALID_HANDLE_VALUE), L"CreateFile.......................... ");

  // Compute the cryptographic hash of the data.
  while (bResult = ReadFile(hDataFile, rgbFile, BUFSIZE, &cbRead, NULL))
  {
       if (cbRead == 0)
        {
           break;
      }
       CheckError(bResult, L"ReadFile............................ ");

      bResult = CryptHashData(
            hHash, 
         rgbFile, 
           cbRead, 
            0
       );
      CheckError(bResult, L"CryptHashData....................... ");
  }
   CheckError(bResult, L"ReadFile............................ ");

  // Open the file with the signature
 hSignatureFile = CreateFileW(
       SignatureFileName,
      GENERIC_READ,
       FILE_SHARE_READ,
        NULL,
       OPEN_EXISTING,
      FILE_FLAG_SEQUENTIAL_SCAN,
      NULL
    );
  CheckError((hSignatureFile != INVALID_HANDLE_VALUE), L"CreateFile.......................... ");

 // Read the signature from the file
 pbBinary = (BYTE *)malloc(BUFSIZE);
 CheckError((BOOL)pbBinary, L"malloc.............................. ");
       
    bResult = ReadFile(hSignatureFile, pbBinary, BUFSIZE, &cbBinary, NULL);
 CheckError(bResult, L"ReadFile............................ ");

  // Get the public key from the certificate
  CryptImportPublicKeyInfo(
       hCryptProv, 
        MY_TYPE,
        &pSignerCert->pCertInfo->SubjectPublicKeyInfo,
        &hPubKey
    );
  CheckError(bResult, L"CryptImportPublicKeyInfo............ ");

  // Verify the signature
 bResult = CryptVerifySignature(
     hHash, 
     pbBinary, 
      cbBinary, 
      hPubKey,
        NULL, 
      0
   );
  CheckError(bResult, L"CryptVerifySignature................ ");

  // Clean up and free memory.
    free(pbBinary);

 CloseHandle(hDataFile);
 CloseHandle(hSignatureFile);

    bResult = CryptDestroyHash(hHash);
  CheckError(bResult, L"CryptDestroyHash.................... ");
 
 bResult = CertFreeCertificateContext(pSignerCert);
  CheckError(bResult, L"CertFreeCertificateContext.......... ");

  bResult = CertCloseStore(
       hStoreHandle, 
      CERT_CLOSE_STORE_CHECK_FLAG
 );
  CheckError(bResult, L"CertCloseStore...................... ");

  bResult = CryptReleaseContext(
      hCryptProv,
     0
   );
  CheckError(bResult, L"CryptReleaseContext................. ");
} 
// End of Verify

</SAMPLE>

 

I hope this helps.

Cheers,

 

Alex (Alejandro Campos Magencio)