Click here to Skip to main content
Click here to Skip to main content

How to use certificate from disk with Microsoft CryptoAPI

, 5 Nov 2010
Rate this:
Please Sign up or sign in to vote.
How to use certificate from disk with Microsoft CryptoAPI

This article describes how to digitally sign data using a certificate which is stored outside the Windows Certificate Store. So it may be distributed as external file or is hardcoded into your application.

The provided code samples are for demonstration purposes only and are not intended to be used in a production system - you should at least add proper error handling and release allocated memory.

Preparing key-pair with self-signed certificate

A key-pair can be generated using the OpenSSL. A list of commands used for generation follows, many special articles with details on how to generate certificate can be found on internet, so I don't mention any details about it here. On the projects website, you can find also links to binaries for the appropriate platform.

  • Generate private key of desired length (1024 bits)
    openssl genrsa -des3 -out server.key 1024
  • Create request for self-signed certificate
    openssl req -new -key server.key -out server.csr
  • Remove password from the private key
    openssl rsa -in server.key -out server2.key
  • Self-sign the certificate request (-days is expiration in days)
    openssl x509 -req -days 365 -in server.csr -signkey server2.key -out server.crt
  • Convert output to PKCS#12 format which we can use in code (-keysig parameter allows us to use key-pair for signing)
    openssl pkcs12 -export -in server.crt -inkey server2.key –keysig -out c:\mykey.p12

Signing data

The final solution is quite easy as you can see in the code below. The only important thing is that your key-pair must be saved in PKCS#12 form (.p12 extension).

CRYPT_DATA_BLOB data;

// load key-pair from disk to memory. This can be quite
// easily changed to have key-pair hardcoded
FILE *fIn = fopen("C:\\mykey.p12", "rb");
fseek(fIn, 0, SEEK_END);
data.cbData = ftell(fIn);
fseek(fIn, 0, SEEK_SET);
data.pbData = (BYTE *)malloc(pData.cbData);
fread(data.pbData, 1, pData.cbData, fIn);
fclose(fIn);

// Convert key-pair data to the in-memory certificate store
// If the P12 file is protected with a password, pass it to
// the function    
HCERTSTORE hCertStore = PFXImportCertStore(&data, L"P12ExportPassword", 0);

// If in-memory certificate store wasn't initialized, try
// empty password (NULL has different meaning than empty string)
if (!hCertStore)
  hCertStore = PFXImportCertStore(&data, L"", 0);
if (!hCertStore)
  hCertStore = PFXImportCertStore(&data, NULL, 0);

// Find the certificate in P12 file (we expect there is only one)    
PCCERT_CONTEXT hContext = CertFindCertificateInStore
    (hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL);

BOOL bFreeHandle;
HCRYPTPROV hProv;
DWORD dwKeySpec;
// acquire private key to this certificate, returned hProv handle must be closed
// if bFreeHandle is TRUE    
BOOL bResult = CryptAcquireCertificatePrivateKey
    (hContext, 0, NULL, &hProv, &dwKeySpec, &bFreeHandle);

const char szBuffToSign[] = "Hello world!";    
DWORD dwSize = 0;

And now, based on our needs sign data as a message, which allows us to include selected certificates:

CRYPT_SIGN_MESSAGE_PARA p;
memset(&p, 0, sizeof(CRYPT_SIGN_MESSAGE_PARA));
p.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
p.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
p.pSigningCert = hContext;
p.HashAlgorithm.pszObjId = szOID_OIWSEC_sha1;
p.cMsgCert = 1;
p.rgpMsgCert = &hContext;
    
DWORD dwSizes[] = {
                    strlen(szBuffToSign)
                  };

const BYTE *byMessageArray[] = { 
                    (const BYTE *) szBuffToSign
};

bResult = CryptSignMessage(&p, FALSE, 1, byMessageArray,  dwSizes, NULL, &dwSize);
    
BYTE *pBuff = (BYTE *) malloc(dwSize);
memset(pBuff, 0, dwSize);
bResult = CryptSignMessage(&p, FALSE, 1, byMessageArray, dwSizes, pBuff, &dwSize);

Or generate a hash from data and sign this hash:

HCRYPTHASH hHash;
CryptCreateHash(hProv, CALG_SHA1, NULL, 0, &hHash);
CryptHashData(hHash, (const BYTE *)szBuffToSign, strlen(szBuffToSign), 0);    
CryptSignHash(hHash, AT_SIGNATURE, "", 0, NULL, &dwSize);

BYTE *pBuff = (BYTE *) malloc(dwSize);
memset(pBuff, 0, dwSize);
CryptSignHash(hHash, AT_SIGNATURE, "", 0, pBuff, &dwSize);

Links

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

voloda2
Software Developer
Czech Republic Czech Republic
I'm software developer since 1996. I started with assembler on Intel 8051 CPUs, during years I was interested in C, C++, Sybase PowerBuilder, PHP, Sybase Anywhere Database, MSSQL server and multiplatform development.
 
Currently I'm developing in C++ and C# (this is my favorit and I spent some time with MCPD achievement). I'm also interested in design patterns.

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 5 Nov 2010
Article Copyright 2010 by voloda2
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid