How to perform Public Key Encryption with RSA using C++ - c++

I am trying to implement asymmetrc encryption into a project. My end goal is to implement EVP Asymmetric Encryption and Decryption of an Envelope but for now I am going to stick with the basics. I am rather new to openssl so please forgive me if there is an obvious answer to this question.
Currently I am following this tutorial here: Simple Public Key Encryption with RSA and OpenSSL
I plan to transmit the RSA public key over a network to a seperate client which will encrypt a message with the generated public key. The encrypted message will then be sent back to the server for decryption.
I am having trouble with encrypting only using the public key instead of using the keypair. For obvious reasons, I don't want to transmit the keypair over the network.
Here is the code that I have so far:
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <stdio.h>
#include <string.h>
#define KEY_LENGTH 2048
#define PUB_EXP 3
#define PRINT_KEYS
#define WRITE_TO_FILE
int main(void) {
size_t pri_len; // Length of private key
size_t pub_len; // Length of public key
char *pri_key; // Private key
char *pub_key; // Public key
char msg[KEY_LENGTH/8]; // Message to encrypt
char *encrypt = NULL; // Encrypted message
char *decrypt = NULL; // Decrypted message
char *err; // Buffer for any error messages
// Generate key pair
printf("Generating RSA (%d bits) keypair...", KEY_LENGTH);
fflush(stdout);
RSA *keypair = RSA_generate_key(KEY_LENGTH, PUB_EXP, NULL, NULL);
// To get the C-string PEM form:
BIO *pri = BIO_new(BIO_s_mem());
BIO *pub = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);
PEM_write_bio_RSAPublicKey(pub, keypair);
//SEND KEY TO CLIENT HERE
//FAKE CLIENT RECIEVE
RSA *keypair2 = NULL;
PEM_read_bio_RSAPublicKey( pub, &keypair2, NULL, NULL);
// Get the message to encrypt
printf("Message to encrypt: ");
fgets(msg, KEY_LENGTH-1, stdin);
msg[strlen(msg)-1] = '\0';
// Encrypt the message using public key only
encrypt = malloc(RSA_size(keypair2));
int encrypt_len;
err = malloc(130);
if((encrypt_len = RSA_public_encrypt(strlen(msg)+1, (unsigned char*)msg, (unsigned char*)encrypt,
keypair2, RSA_PKCS1_OAEP_PADDING)) == -1) {
ERR_load_crypto_strings();
ERR_error_string(ERR_get_error(), err);
fprintf(stderr, "Error encrypting message: %s\n", err);
goto free_stuff;
}
#ifdef WRITE_TO_FILE
// Write the encrypted message to a file
FILE *out = fopen("out.bin", "w");
fwrite(encrypt, sizeof(*encrypt), RSA_size(keypair), out);
fclose(out);
printf("Encrypted message written to file.\n");
free(encrypt);
encrypt = NULL;
// Read it back
printf("Reading back encrypted message and attempting decryption...\n");
encrypt = malloc(RSA_size(keypair));
out = fopen("out.bin", "r");
fread(encrypt, sizeof(*encrypt), RSA_size(keypair), out);
fclose(out);
#endif
//SEND MESSAGE BACK TO CLIENT FOR DECRYPT
// Decrypt it
decrypt = malloc(encrypt_len);
if(RSA_private_decrypt(encrypt_len, (unsigned char*)encrypt, (unsigned char*)decrypt,
keypair, RSA_PKCS1_OAEP_PADDING) == -1) {
ERR_load_crypto_strings();
ERR_error_string(ERR_get_error(), err);
fprintf(stderr, "Error decrypting message: %s\n", err);
goto free_stuff;
}
printf("Decrypted message: %s\n", decrypt);
free_stuff:
RSA_free(keypair);
BIO_free_all(pub);
BIO_free_all(pri);
free(pri_key);
free(pub_key);
free(encrypt);
free(decrypt);
free(err);
return 0;
}
How could I achieve encrypting data only using the public key rather than the whole keypair?
Thanks

Related

Loading certificates from string buffers into chain for client verification using OpenSSL

I am trying to use SSL client verification to connect to a remote AWS IoT endpoint. I have three files:
- CA Root Certificate File
- Certificate File
- Private Key
The connection to the endpoint is successful if I use SSL_CTX_load_verify_locations, SSL_CTX_use_certificate_chain_file and SSL_CTX_use_PrivateKey_file.
if (!SSL_CTX_load_verify_locations(p_ssl_context_, root_ca_location_.c_str(), NULL)) {
AWS_LOG_ERROR(OPENSSL_WRAPPER_LOG_TAG, " Root CA Loading error");
return ResponseCode::NETWORK_SSL_ROOT_CRT_PARSE_ERROR;
}
// TODO: streamline error codes for TLS
if (0 < device_cert_location_.length() && 0 < device_private_key_location_.length()) {
AWS_LOG_DEBUG(OPENSSL_WRAPPER_LOG_TAG, "Device crt : %s", device_cert_location_.c_str());
if (!SSL_CTX_use_certificate_chain_file(p_ssl_context_, device_cert_location_.c_str())) {
AWS_LOG_ERROR(OPENSSL_WRAPPER_LOG_TAG, " Device Certificate Loading error");
return ResponseCode::NETWORK_SSL_DEVICE_CRT_PARSE_ERROR;
}
AWS_LOG_DEBUG(OPENSSL_WRAPPER_LOG_TAG, "Device privkey : %s", device_private_key_location_.c_str());
if (1 != SSL_CTX_use_PrivateKey_file(p_ssl_context_,
device_private_key_location_.c_str(),
SSL_FILETYPE_PEM)) {
AWS_LOG_ERROR(OPENSSL_WRAPPER_LOG_TAG, " Device Private Key Loading error");
return ResponseCode::NETWORK_SSL_KEY_PARSE_ERROR;
}
}
certificates_read_flag_ = true;
However, the call doesn't work if I construct the chain manually by adding the certificates using string buffers. The error returned is "An unknown occurred while waiting for the TLS handshake to complete.".
X509_STORE* store = SSL_CTX_get_cert_store(p_ssl_context_);
X509 *rootCACertificate, *deviceCertificate;
RSA *privateKey;
BIO *bio;
std::string rootCACertificateBuffer;
std::string deviceCertificateBuffer;
std::string privateKeyBuffer;
rootCACertificateBuffer =
"-----BEGIN CERTIFICATE-----\n"
"-----END CERTIFICATE-----\n";
deviceCertificateBuffer =
"-----BEGIN CERTIFICATE-----\n"
"-----END CERTIFICATE-----\n";
privateKeyBuffer =
"-----BEGIN RSA PRIVATE KEY-----\n"
"-----END RSA PRIVATE KEY-----\n";
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, rootCACertificateBuffer.c_str());
rootCACertificate = PEM_read_bio_X509(bio, NULL, 0, NULL);
X509_STORE_add_cert(store, rootCACertificate);
BIO_free(bio);
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, deviceCertificateBuffer.c_str());
deviceCertificate = PEM_read_bio_X509(bio, NULL, 0, NULL);
X509_STORE_add_cert(store, deviceCertificate);
BIO_free(bio);
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, privateKeyBuffer.c_str());
privateKey = PEM_read_bio_RSAPrivateKey(bio, NULL, 0, NULL);
SSL_CTX_use_RSAPrivateKey(p_ssl_context_, privateKey);
BIO_free(bio);
SSL_CTX_build_cert_chain(p_ssl_context_,SSL_BUILD_CHAIN_FLAG_CHECK );
certificates_read_flag_ = true;

OpenSSL RSA Public/Private Encryption/Decryption with pkcs12 file

I am porting a service from C# to C++ - which involves RSA encryption. Using OpenSSL 1.0.2. (I wrote my first line of C++ two weeks ago - so please bear with me.)
I am trying to encrypt/decrypt data - using RSA. Encryption uses a public key which is retrieved by invoking a server via HTTPS (The public key of the SSL certificate is used).
Decryption happens on the server - which loads the private key from a PFX file (PKCS12).
I am having problems with decryption - error:0407109F:rsa routines:RSA_padding_check_PKCS1_type_2:pkcs decoding error
What am I doing wrong?
Encryption - getting public key from server (HTTPS)
int MyUtil::EncryptWithPublicKeyRetrievedFromServer(int length, unsigned char* bytesToEncrypt, unsigned char* output){
//Get Certificate from server
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
boost::asio::io_service io_service;
ctx.set_default_verify_paths();
client myClient = client(io_service, ctx);
auto cert = myClient.GetX509Certificate("www.myHttpsDomain.com");
//Get public key
auto key = X509_get_pubkey(cert);
auto rsa = EVP_PKEY_get1_RSA(key);
//Encrypt
return RSA_public_encrypt(length, bytesToEncrypt, output, rsa, RSA_PKCS1_PADDING);
}
Decryption - using private key
int MyUtil::DecryptWithPrivateKey(int length, unsigned char* bytesToDecrypt, unsigned char *output){
auto filePath = "C:\\myCertificateWithPrivateAndPublicKey.pfx";
auto pass = "MySecretPassword";
FILE *fp;
EVP_PKEY *pkey;
X509 *cert;
STACK_OF(X509) *ca = NULL;
PKCS12 *p12;
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
#pragma warning (disable : 4996) //MS complains about fopen
if (!(fp = fopen(filePath, "rb"))) {
fprintf(stderr, "Error opening file %s\n", filePath);
exit(1);
}
p12 = d2i_PKCS12_fp(fp, NULL);
fclose(fp);
if (!p12) {
fprintf(stderr, "Error reading PKCS#12 file\n");
ERR_print_errors_fp(stderr);
exit(1);
}
//Parse PKCS12 (PFX) file in order to get private key
if (!PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
fprintf(stderr, "Error parsing PKCS#12 file\n");
ERR_print_errors_fp(stderr);
exit(1);
}
PKCS12_free(p12);
//Decrypt
auto rsa = EVP_PKEY_get1_RSA(pkey);
int sizeOfDecryptedData;
if (sizeOfDecryptedData = RSA_private_decrypt(length, bytesToDecrypt, output, rsa, RSA_PKCS1_PADDING) == -1)
{
auto err = new char;
ERR_load_crypto_strings();
ERR_error_string(ERR_get_error(), err);
fprintf(stderr, "Error encrypting message: %s\n", err);
//--> error:0407109F:rsa routines:RSA_padding_check_PKCS1_type_2:pkcs decoding error
delete err;
}
sk_X509_pop_free(ca, X509_free);
X509_free(cert);
EVP_PKEY_free(pkey);
fclose(fp);
return sizeOfDecryptedData;
}
I also tried loading the private key through the PEM format and then decrypt
int MyUtil::DecryptWithPrivateKey(int length, unsigned char* bytesToDecrypt, unsigned char *output){
char *private_key_file_name = "C:\\privatekey.cer";
#pragma warning (disable : 4996) //MS complains about fopen
FILE *fp = fopen(private_key_file_name, "r");
RSA *rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
fclose(fp);
if (sizeOfDecryptedData = RSA_private_decrypt(length, bytesToDecrypt, output, rsa, RSA_PKCS1_PADDING) == -1)
{
auto err = new char;
ERR_load_crypto_strings();
ERR_error_string(ERR_get_error(), err);
fprintf(stderr, "Error encrypting message: %s\n", err);
//--> error:0407109F:rsa routines:RSA_padding_check_PKCS1_type_2:pkcs decoding error
delete err;
}
return decryptedBytes;
}

How do I get the RSA* object from a char array that contains the public key in OpenSSL?

Im trying to encrypt and decrypt messages while storing the private and public keys on char vectors. I have tried d2i_PublicKey(...) and using EVP_PKEY objects in EVP_set1_RSA(...). I also do not know what are all the parameters in EVP_set1_RSA(...). Please help. Here is my code:
#include <stdio.h>
//RSA
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <arpa/inet.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#define RSA_KEY_LENGTH 2048
#define PUB_EXP 3
#define PRINT_KEYS
//RSA
int main()
{
printf("\ngenerating keys...\n");
RSA *keypair = RSA_generate_key(RSA_KEY_LENGTH, PUB_EXP, NULL, NULL);
// ---------
printf("Converting Keys to char array..\n");
char *pri_key = NULL; // Private key
char *pub_key = NULL; // Public key
size_t pri_len; // Length of private key
size_t pub_len; // Length of public key
BIO *pri = BIO_new(BIO_s_mem());
BIO *pub = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);
PEM_write_bio_RSAPublicKey(pub, keypair);
pri_len = BIO_pending(pri);
pub_len = BIO_pending(pub);
pri_key = (char*)malloc(pri_len + 1);
pub_key = (char*)malloc(pub_len + 1);
BIO_read(pri, pri_key, pri_len);
BIO_read(pub, pub_key, pub_len);
pri_key[pri_len] = '\0';
pub_key[pub_len] = '\0';
// ---------
char msg[RSA_KEY_LENGTH/8] = "HOLA, ESPERO QUE ME ENCRIPTES";
char *encrypt = NULL; // Encrypted message
char *decrypt = NULL; // Decrypted message
printf("encrypting: %s\n", msg);
/*
* Here I want to obtain an RSA *PublicKey to use it for the encryption
*/
int encrypt_len;
err = (char*)malloc(130);
printf("++++\n");
if((encrypt_len = RSA_public_encrypt(strlen(msg), (unsigned char*)msg, (unsigned char*)encrypt, PublicKey, RSA_PKCS1_OAEP_PADDING)) == -1) {
printf("err++++\n");
ERR_load_crypto_strings();
ERR_error_string(ERR_get_error(), err);
fprintf(stderr, "Error encrypting message: %s\n", err);
}
return 0;
}
I've found a solution to this issue among other Stack-Overflow posts and namely here :Reading Public/Private Key from Memory with OpenSSL
The answer you waere looking for is answered by #SquareRootOfTwentyThree is his last line of code,
After extracting the Public key into a BIO variable called pub:
PEM_write_bio_RSAPublicKey(pub, keypair);
create a RSA variable and put pub inside it:
RSA *keypair2 = NULL;
PEM_read_bio_RSAPublicKey( pub, &keypair2, NULL, NULL);
After you've done this you can successfully encrypt the message as usual, using keypair2:
Encryption:
encrypt = (char*)malloc(RSA_size(keypair));
int encrypt_len;
err = (char*)malloc(130);
if((encrypt_len = RSA_public_encrypt(strlen(msg)+1, (unsigned char*)msg, (unsigned char*)encrypt, keypair2 ,RSA_PKCS1_OAEP_PADDING)) == -1) {
ERR_load_crypto_strings();
ERR_error_string(ERR_get_error(), err);
fprintf(stderr, "Error encrypting message: %s\n", err);
}
you can then decrypt it as usual, using the original keypair, without having to use it at your first encryption
Decryption:
decrypt = (char*)malloc(encrypt_len);
if(RSA_private_decrypt(encrypt_len, (unsigned char*)encrypt, (unsigned char*)decrypt, keypair, RSA_PKCS1_OAEP_PADDING) == -1) {
ERR_load_crypto_strings();
ERR_error_string(ERR_get_error(), err);
fprintf(stderr, "Error decrypting message: %s\n", err);
}
This might help if you want to transfer the "pub" variable over a network, use it to encrypt a message, then send the encrypted data back to the original machine to get it decrypted.
If you really want to use char variables, as you said in your question, you can of course copy the memory as raw to a char variable (from the BIO one) using memcpy, but don't forget to add the "\0" at the end, here, this post should help: Separating public and private keys from RSA keypair variable

More Cpp, Winsock, RSA and OpenSSL

First off I want to warn you there is alot of code here! I want the client to connect to the server using RSA with the OpenSSL library. When I launch the server everything is fine but when I try and connect with the client the connection failed with Error: error 1408A0C1: SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher. Thanks for your consideration but I have to warn you again there is alot of code here. If you are interested I am creating a TCP connection to a Java applet protected with RSA. This is not my program I found it all jumbled on the 'net. If you could add your own simple source for this purpose in the comments I would appreciate it!
// THIS IS THE CLIENT FOR THE CONNECTION
#include <openssl/bio.h> // BIO objects for I/O
#include <openssl/ssl.h> // SSL and SSL_CTX for SSL connections
#include <openssl/err.h> // Error reporting
#include <stdio.h> // If you don't know what this is for stop reading now.
void openssltest(void);
int main(int argc, char** argv) {
CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
SSL_library_init(); // Initialize OpenSSL's SSL libraries
SSL_load_error_strings(); // Load SSL error strings
ERR_load_BIO_strings(); // Load BIO error strings
OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
openssltest(); // We'll define this later.
return 0;
}
void openssltest() {
// Set up a SSL_CTX object, which will tell our BIO object how to do its work
SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method());
// Create a SSL object pointer, which our BIO object will provide.
SSL* ssl;
// Create our BIO object for SSL connections.
BIO* bio = BIO_new_ssl_connect(ctx);
// Failure?
if (bio == NULL) {
printf("Error creating BIO!\n");
ERR_print_errors_fp(stderr);
// We need to free up the SSL_CTX before we leave.
SSL_CTX_free(ctx);
return;
}
// Makes ssl point to bio's SSL object.
BIO_get_ssl(bio, &ssl);
// Set the SSL to automatically retry on failure.
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
// We're connection to google.com on port 443.
BIO_set_conn_hostname(bio, "127.0.0.1:6789");
// Same as before, try to connect.
if (BIO_do_connect(bio) <= 0) {
printf("Failed to connect!");
BIO_free_all(bio);
SSL_CTX_free(ctx);
return;
}
// Now we need to do the SSL handshake, so we can communicate.
if (BIO_do_handshake(bio) <= 0) {
printf("Failed to do SSL handshake!");
BIO_free_all(bio);
SSL_CTX_free(ctx);
return;
}
// Create a buffer for grabbing information from the page.
char buf[1024];
memset(buf, 0, sizeof(buf));
// Create a buffer for the reqest we'll send to the server
char send[1024];
memset(send, 0, sizeof(send));
// Create our GET request.
strcat(send, "GET / HTTP/1.1\nHost:google.com\nUser Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)\nConnection: Close\n\n");
// BIO_puts sends a null-terminated string to the server. In this case it's our GET request.
BIO_puts(bio, send);
// Loop while there's information to be read.
while (1) {
// BIO_read() reads data from the server into a buffer. It returns the number of characters read in.
int x = BIO_read(bio, buf, sizeof(buf) - 1);
// If we haven't read in anything, assume there's nothing more to be sent since we used Connection: Close.
if (x == 0) {
break;
}
// If BIO_read() returns a negative number, there was an error
else if (x < 0) {
// BIO_should_retry lets us know if we should keep trying to read data or not.
if (!BIO_should_retry(bio)) {
printf("\nRead Failed!\n");
BIO_free_all(bio);
SSL_CTX_free(ctx);
return;
}
}
// We actually got some data, without errors!
else {
// Null-terminate our buffer, just in case
buf[x] = 0;
// Echo what the server sent to the screen
printf("%s", buf);
}
}
// Free up that BIO object we created.
BIO_free_all(bio);
// Remember, we also need to free up that SSL_CTX object!
SSL_CTX_free(ctx);
// Return.
return;
}
// THIS IS THE SERVER FOR THE CONNECTION
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <iostream>
#include <stdio.h>
#include <winsock2.h>
#define PASSWORD "passme"
void serverThread();
int main(int argc, char** argv) {
CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
SSL_library_init(); // Initialize OpenSSL's SSL libraries
SSL_load_error_strings(); // Load SSL error strings
ERR_load_BIO_strings(); // Load BIO error strings
OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
serverThread();
return 0;
}
void serverThread() {
// First, we need to initialize Winsock.
WSADATA wsadata;
int ret = WSAStartup(0x101, &wsadata);
if (ret != 0) {
printf("WSAStartup() failed with: %d!\n", GetLastError());
return;
}
// Next we need to create a server socket.
SOCKET server = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in sockaddrin;
// Internet socket
sockaddrin.sin_family = AF_INET;
// Accept any IP
sockaddrin.sin_addr.s_addr = INADDR_ANY;
// Use port 6789
sockaddrin.sin_port = htons(6789);
// Valid socket?
if (server == INVALID_SOCKET) {
printf("Error creating server socket!");
return;
}
// Now bind to the port
ret = bind(server, (sockaddr*) &(sockaddrin), sizeof(sockaddrin));
if (ret != 0) {
printf("Error binding to port!\n");
return;
}
// Start listening for connections
// Second param is max number of connections
ret = listen(server, 50);
if (ret != 0) {
printf("Error listening for connections!\n");
return;
}
// Set up to accept connections
SOCKET client;
sockaddr_in clientsockaddrin;
int len = sizeof(clientsockaddrin);
printf("Server ready to accept connections!\n");
while (1) {
// Block until a connection is ready
client = accept(server, (sockaddr*) &clientsockaddrin, &len);
printf("Connection recieved from %s!\n", inet_ntoa(clientsockaddrin.sin_addr));
// Notice that we use server_method instead of client_method
SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
BIO* bio = BIO_new_file("dh1024.pem", "r");
// Did we get a handle to the file?
if (bio == NULL) {
printf("Couldn't open DH param file!\n");
break;
}
// Read in the DH params.
DH* ret = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
// Free up the BIO object.
BIO_free(bio);
// Set up our SSL_CTX to use the DH parameters.
if (SSL_CTX_set_tmp_dh(ctx, ret) < 0) {
printf("Couldn't set DH parameters!\n");
break;
}
// Now we need to generate a RSA key for use.
// 1024-bit key. If you want to use something stronger, go ahead but it must be a power of 2. Upper limit should be 4096.
RSA* rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL);
// Set up our SSL_CTX to use the generated RSA key.
if (!SSL_CTX_set_tmp_rsa(ctx, rsa)) {
printf("Couldn't set RSA key!\n");
// We don't break out here because it's not a requirement for the RSA key to be set. It does help to have it.
}
// Free up the RSA structure.
RSA_free(rsa);
SSL_CTX_set_cipher_list(ctx, "ALL");
// Set up our SSL object as before
SSL* ssl = SSL_new(ctx);
// Set up our BIO object to use the client socket
BIO* sslclient = BIO_new_socket(client, BIO_NOCLOSE);
// Set up our SSL object to use the BIO.
SSL_set_bio(ssl, sslclient, sslclient);
// Do SSL handshaking.
int r = SSL_accept(ssl);
// Something failed. Print out all the error information, since all of it may be relevant to the problem.
if (r != 1) {
printf("SSL_accept() returned %d\n", r);
printf("Error in SSL_accept(): %d\n", SSL_get_error(ssl, r));
char error[65535];
ERR_error_string_n(ERR_get_error(), error, 65535);
printf("Error: %s\n\n", error);
ERR_print_errors(sslclient);
int err = WSAGetLastError();
printf("WSA: %d\n", err);
break;
}
}
}
int password_callback(char* buffer, int num, int rwflag, void* userdata) {
if (num < (strlen(PASSWORD) + 1)) {
return(0);
}
strcpy(buffer, PASSWORD);
return strlen(PASSWORD);
}
int verify_callback(int ok, X509_STORE_CTX* store) {
char data[255];
if (!ok) {
X509* cert = X509_STORE_CTX_get_current_cert(store);
int depth = X509_STORE_CTX_get_error_depth(store);
int err = X509_STORE_CTX_get_error(store);
printf("Error with certificate at depth: %d!\n", depth);
X509_NAME_oneline(X509_get_issuer_name(cert), data, 255);
printf("\tIssuer: %s\n", data);
X509_NAME_oneline(X509_get_subject_name(cert), data, 255);
printf("\tSubject: %s\n", data);
printf("\tError %d: %s\n", err, X509_verify_cert_error_string(err));
}
return ok;
}
Usually that error indicates you have not configured an RSA key in your server. In order to use RSA ciphersuites your server must have a certificate with an RSA key properly configured.

Load an PEM encoded X.509 certificate into Windows CryptoAPI

I need to load a PEM encoded X.509 certificate into a Windows Crypto API context to use with C++. They are the ones that have -----BEGIN RSA XXX KEY----- and -----END RSA XXX KEY-----. I found examples for Python and .NET but they use specific functions I can't relate to the plain Windows Crypto API.
I understand how to encrypt/decrypt once I've got a HCRYPTKEY.
BUT, I just don't get how to import the Base64 blob in the .PEM file(s) and get a HCRYPTKEY that I can use out of it.
I have that strange feeling that there is more to it than simply calling CryptDecodeObject().
Any pointers that can put me on track? I've already lost two days doing "trial & error" programming and getting nowhere.
KJKHyperion said in his answer:
I discovered the "magic" sequence of calls to import a RSA public key in PEM format. Here you go:
decode the key into a binary blob with CryptStringToBinary; pass CRYPT_STRING_BASE64HEADER in dwFlags
decode the binary key blob into a CERT_PUBLIC_KEY_INFO with CryptDecodeObjectEx; pass X509_ASN_ENCODING in dwCertEncodingType and X509_PUBLIC_KEY_INFO in lpszStructType
decode the PublicKey blob from the CERT_PUBLIC_KEY_INFO into a RSA key blob with CryptDecodeObjectEx; pass X509_ASN_ENCODING in dwCertEncodingType and RSA_CSP_PUBLICKEYBLOB in lpszStructType
import the RSA key blob with CryptImportKey
This sequence really helped me understand what's going on, but it didn't work for me as-is. The second call to CryptDecodeObjectEx gave me an error:
"ASN.1 bad tag value met".
After many attempts at understanding Microsoft documentation, I finally realized that the output of the fist decode cannot be decoded as ASN again, and that it is actually ready for import. With this understanding I found the answer in the following link:
http://www.ms-news.net/f2748/problem-importing-public-key-4052577.html
Following is my own program that imports a public key from a .pem file to a CryptApi context:
int main()
{
char pemPubKey[2048];
int readLen;
char derPubKey[2048];
size_t derPubKeyLen = 2048;
CERT_PUBLIC_KEY_INFO *publicKeyInfo;
int publicKeyInfoLen;
HANDLE hFile;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
/*
* Read the public key cert from the file
*/
hFile = CreateFileA( "c:\\pub.pem", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if ( hFile == INVALID_HANDLE_VALUE )
{
fprintf( stderr, "Failed to open file. error: %d\n", GetLastError() );
}
if ( !ReadFile( hFile, pemPubKey, 2048, &readLen, NULL ) )
{
fprintf( stderr, "Failed to read file. error: %d\n", GetLastError() );
}
/*
* Convert from PEM format to DER format - removes header and footer and decodes from base64
*/
if ( !CryptStringToBinaryA( pemPubKey, 0, CRYPT_STRING_BASE64HEADER, derPubKey, &derPubKeyLen, NULL, NULL ) )
{
fprintf( stderr, "CryptStringToBinary failed. Err: %d\n", GetLastError() );
}
/*
* Decode from DER format to CERT_PUBLIC_KEY_INFO
*/
if ( !CryptDecodeObjectEx( X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, derPubKey, derPubKeyLen,
CRYPT_ENCODE_ALLOC_FLAG, NULL, &publicKeyInfo, &publicKeyInfoLen ) )
{
fprintf( stderr, "CryptDecodeObjectEx 1 failed. Err: %p\n", GetLastError() );
return -1;
}
/*
* Acquire context
*/
if( !CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) )
{
{
printf( "CryptAcquireContext failed - err=0x%x.\n", GetLastError() );
return -1;
}
}
/*
* Import the public key using the context
*/
if ( !CryptImportPublicKeyInfo( hProv, X509_ASN_ENCODING, publicKeyInfo, &hKey ) )
{
fprintf( stderr, "CryptImportPublicKeyInfo failed. error: %d\n", GetLastError() );
return -1;
}
LocalFree( publicKeyInfo );
/*
* Now use hKey to encrypt whatever you need.
*/
return 0;
}
I discovered the "magic" sequence of calls to import a RSA public key in PEM format. Here you go:
decode the key into a binary blob with CryptStringToBinary; pass CRYPT_STRING_BASE64HEADER in dwFlags
decode the binary key blob into a CERT_PUBLIC_KEY_INFO with CryptDecodeObjectEx; pass X509_ASN_ENCODING in dwCertEncodingType and X509_PUBLIC_KEY_INFO in lpszStructType
decode the PublicKey blob from the CERT_PUBLIC_KEY_INFO into a RSA key blob with CryptDecodeObjectEx; pass X509_ASN_ENCODING in dwCertEncodingType and RSA_CSP_PUBLICKEYBLOB in lpszStructType
import the RSA key blob with CryptImportKey
I'm currently facing the same difficulty. I haven't finished coding a solution but as I understand it you need to strip off the ----- BEGIN etc ----- and ----- END etc ------ tags and decode the Base64.
This leaves you with a DER encoded string, which you need to parse to get the modulus and public exponent. From those you can populate the PUBLICKEYSTRUC and RSAPUBKEY structures. Good luck ;-)