Click here to Skip to main content
15,884,176 members
Articles / All Topics

How to Use Certificate from Disk with Microsoft CryptoAPI

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
5 Nov 2010CPOL1 min read 25.1K   5   2
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 the internet, so I don't mention any details about it here. On the projects website, you can also find 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).

C++
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:

C++
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:

C++
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)


Written By
Team Leader NCR
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

 
QuestionComplete code for this article? Pin
Member 1163332722-Apr-15 4:47
Member 1163332722-Apr-15 4:47 
AnswerRe: Complete code for this article? Pin
voloda222-Apr-15 9:35
voloda222-Apr-15 9:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.