Decrypt my World

Cryptography, Security, Debugging and more!

How to sign and verify with CryptoAPI and a user certificate

How to sign and verify with CryptoAPI and a user certificate

  • Comments 8

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)

  • thanks very much for this article.

    this is exactly what i needed!

  • Hi,

    I'm new to the cryptoAPI. I am trying to use it for automated code signing 'a la' signtool.exe but your code places the signature in a distinct file.  What should be changed to add the signature to the file ?

    Is there a way to strip a file of its signature in order to confirm that the signed file has the same "content " as its unsigned original ?

  • Hi dtieuf,

    To sign binaries like you do with signtool, you can check these posts of mine:

    How to sign EXE files with an Authenticode certificate (VB.NET)

    blogs.msdn.com/.../how-to-sign-exe-files-with-an-authenticode-certificate-vb-net.aspx

    How to sign EXE files with an Authenticode certificate (part 2)

    blogs.msdn.com/.../how-to-sign-exe-files-with-an-authenticode-certificate-part-2.aspx

    Thx,

    Alex

  • Can you tell how can i attach signed hash to a binary or executable. I dont want to use a second file for storing hash. Is there any Crypto API available in C.

    As you solved the same problem in VB Net , can you tell how can i do the same thing in C++

  • Hi vondamn,

    To sign binaries programmatically, we use this:

    How to sign EXE files with an Authenticode certificate (part 2)blogs.msdn.com/.../how-to-sign-exe-files-with-an-authenticode-certificate-part-2.aspx

  • Thank you sir.

    I am able to sign my executable with Authenticode Certificate.

    Now i need to verify this signed executable (done via SignerSignEx API) with the public key of the certificate.

    Is there any link which does the same.

  • Hi vondamn,

    I don't have a sample for that, but if I recall well, you have to use WinVerifyTrust API for that (msdn.microsoft.com/.../aa388208(v=VS.85).aspx)

  • Hi  Alejandro Campos Magencio,

    I appreciate if you can tell me WinAPI which extracts the sign data from the signed file i..e i have a signed file and want to extracts its signed blob.

    Thanks a lot.

Page 1 of 1 (8 items)
Leave a Comment
  • Please add 4 and 1 and type the answer here:
  • Post
Translate This Page