I'm using the OpenSSL CAPI engine and ENGINE_load_private_key to load the private key for a certificate in the windows certificate store.
#include "stdafx.h"
#include <iostream>
#include <openssl/ssl.h>
#include <openssl/engine.h>
#include <openssl/pem.h>
//Note: using openssl-1.0.1c
// libs: crypt32.lib libeay32.lib ssleay32.lib ws2_32.lib
// Note: Comment this to have the key saved with PEM_write_bio_PrivateKey
//#define SAVE_AS_RSA
void saveToPEMFile(EVP_PKEY* pkey, X509* cert)
{
std::cout << "TEST Save CERT and PKEY to PEM file" << std::endl;
if (!pkey || !cert)
return;
BIO* pem = BIO_new_file("./test.pem", "wt");
std::cout << "Saving private key type " << pkey->type << "..." << std::endl;
const EVP_CIPHER* enc=EVP_des_ede3_cbc();
#ifdef SAVE_AS_RSA
RSA* rsa = EVP_PKEY_get1_RSA(pkey);
if(!PEM_write_bio_RSAPrivateKey(pem, rsa, enc, NULL, 0, NULL, NULL))
#else
if(!PEM_write_bio_PrivateKey(pem, pkey, enc, NULL, 0, NULL, NULL))
#endif
std::cout << "couldn't save the private key" <<
std::endl;
// Write the certificate
if (!PEM_write_bio_X509(pem, cert))
std::cout << "couldn't save the certificate" << std::endl;
BIO_free(pem);
std::cout << "TEST Save CERT and PKEY to PEM file done..." << std::endl;
}
void readPKEY()
{
std::cout << "TEST Reading private key" << std::endl;
BIO* pem = BIO_new_file("./test.pem", "rt");
#ifdef SAVE_AS_RSA
// Note:
// This fails in OpenSSL, in d2i_PrivateKey when
// calling
// ameth->old_priv_decode(ret, pp, length)
//
with
// RSAerr(RSA_F_OLD_RSA_PRIV_DECODE, ERR_R_RSA_LIB);
RSA* ret = PEM_read_bio_RSAPrivateKey(pem, NULL, NULL, NULL);
if (ret)
std::cout << "Read RSA private key OK" << std::endl;
else
#else
//Note: This fails in EVP_PKCS82PKEY when calling
// pkey->ameth->priv_decode(pkey, p8)
// Error:
// EVPerr(EVP_F_EVP_PKCS82PKEY,
// EVP_R_PRIVATE_KEY_DECODE_ERROR);
EVP_PKEY* ret = PEM_read_bio_PrivateKey(pem, NULL, NULL, NULL);
if (ret)
std::cout << "Private key type " << ret->type << " read OK" << std::endl;
else
#endif
std::cout << "couldn't read the private key" << std::endl;
BIO_free(pem);
#ifdef SAVE_AS_RSA
RSA_free(ret);
#else
EVP_PKEY_free(ret);
#endif
std::cout << "TEST Reading private key done..." << std::endl;
}
void saveAndReadAsPUBKEY(EVP_PKEY* pkeyToSave)
{
std::cout << "TEST Saving and reading as PUB key" << std::endl;
if (!pkeyToSave)
return;
// Open file for write
BIO* pem = BIO_new_file("./testPUB.pem", "wt");
if(!PEM_write_bio_PUBKEY(pem, pkeyToSave))
std::cout << "couldn't save as PUB key" << std::endl;
else
std::cout << "Saved as PUB key OK" << std::endl;
BIO_free(pem);
// Open file for read
pem = BIO_new_file("./testPUB.pem", "rt");
EVP_PKEY* pubKey = PEM_read_bio_PUBKEY(pem, NULL, NULL, NULL);
if(!pubKey)
std::cout << "couldn't read as PUB key" << std::endl;
else
std::cout << "PUB key read OK" << std::endl;
EVP_PKEY_free(pubKey);
BIO_free(pem);
std::cout << "TEST Saving and reading as PUB key done..." << std::endl;
}
// Note: I have generated a certificate with openssl
// 1) The RSA key 1024 bits
// openssl genrsa -des3 -out test.key 1024
//
// 2) the request + sign certificate
// openssl req -new -key test.key -out cert.csr
//
openssl x509 -req -days 365 -in cert.csr -signkey test.key -out cert.crt
//
// 3) Created the certificate
// type cert.crt cert.csr test.key > cert.pem
//
// Imported the certificate to windows store:
// 1) Converted the certificate to PFX with openSSL
// openssl pkcs12 -export -in cert.pem -out cert.pfx
//
// 2) Imported the cert.pfx into windows certificate store (from the Certificates MMC snapin)
const char cert[] = "PUT THE CERTNAME HERE";
int _tmain(int argc, _TCHAR* argv[])
{
// Using the CAPI engine for OpenSSL
ENGINE_load_capi();
ENGINE* e = ENGINE_by_id("capi");
if(SSL_library_init())
{
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
}
else
return 1;
if(ENGINE_init(e))
{
EVP_PKEY* pStoreKey = NULL;// is this really the private key ?
X509* pStoreCert = NULL; // will this contain the certificate chain also ??
//Note:
// To load the certificate I had to add my own implementation
// for capi_load_server_certificate callback because it is not
// done, not even in openssl-1.0.1c
pStoreCert= ENGINE_load_server_certificate(e, cert, NULL, NULL);
// Load the private key with the OpenSSL CAPI engine
pStoreKey = ENGINE_load_private_key(e, cert, NULL, NULL);
saveToPEMFile(pStoreKey, pStoreCert);
// Note:
// 1) looking into ENGINE_load_private_key I have noticed that the
// key is exported as a PUBLICKEYBLOB and that only the exponent
// and the modulus are set into the returned RSA structure - maybe
// this is why is too small in comparision with RSA private key genereted
// by OpenSSL
// 2) Passing the EVP_PKEY* directly as memory buffer to boost::asio::ssl
// seems to work perfectly (the SSL communnication between client-server
// works) - probably because the RSA keys can be used interchangeably
readPKEY();
saveAndReadAsPUBKEY(pStoreKey);
EVP_PKEY_free(pStoreKey);
X509_free(pStoreCert);
}
ENGINE_cleanup();
ENGINE_finish(e);
ENGINE_free(e);
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
ERR_remove_thread_state(0);
EVP_cleanup();
}