Writing RSA Private Key to PEM File with Passphrase - c++

I am writing a C++ function to create an RSA key pair and store the private key in a PEM file. I use code as in the following example:
static unsigned char passphrase[] = "0123456789";
int keySize = 2048;
int keyExponent = 65537;
BIGNUM *bn;
. . .
bn = BN_new();
BN_set_word(bn, keyExponent);
rsa = RSA_new();
RSA_generate_key_ex(rsa, keySize, bn, nullptr);
FILE* fp = fopen(fileName, "w");
PEM_write_RSAPrivateKey(fp, rsa, EVP_des_ede3_cbc(), passphrase, 10, nullptr, nullptr);
This code works as expected but now I have been asked to store the private key without passphrase protection. Is there a way to get function PEM_write_RSAPrivateKey not to use a passphrase? One option I tried is to change the function call as follows:
outcome = PEM_write_RSAPrivateKey(fp, rsa, EVP_des_ede3_cbc(), nullptr, 0, nullptr, nullptr);
But this results in a prompt to the user to enter a passphrase (which is exactly as stated in the documentation of function PEM_write_RSAPrivateKey). I also tried using a passphrase call-back which returns zero (to signify that there is no passphrase) but this results in PEM_write_RSAPrivateKey returning an error.
What else can I do to have a private key stored in a PEM file without passphrase? I assume that this must somehow be possible because I can do it using openssl commands such as: openssl genrsa -out tmsPrivKey.pem 2048.

The third parameter is the encryption cipher to use to encrypt the private key with. So all you need to do is pass a nullptr to the function.
e.g.
outcome = PEM_write_RSAPrivateKey(fp, rsa, nullptr, nullptr, 0, nullptr, nullptr);
From the documentation:
The PEM functions which write private keys take an enc parameter which
specifies the encryption algorithm to use, encryption is done at the
PEM level. If this parameter is set to NULL then the private key is
written in unencrypted form.

Related

How to generate a secure random STRING (Key and IV) for AES-256-CBC WinApi ? [C/C++]

I need to generate (in C/C++) a secure random string (32byte) to use as Key and another (16byte) to use as IV for AES-256-CBC encryption using WinApi. The problem is that I need to save the generated string in a text file in order to manually test the decryption using OpenSSL in terminal.
What can I use to generate the secure random string other than CryptGenRandom? The problem of CryptGenRandom is that it generates a random byte sequence that I can't save/use as OpenSSL input because it's not an ASCII text:
openssl aes-256-cbc -d -K "..." -iv ".." -in encrypted.txt
Is there an alternative?
This is my working code:
// handles for csp and key
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
BYTE *szKey = (BYTE*)calloc(DEFAULT_AES_KEY_SIZE + 1, sizeof(BYTE));
BYTE *szIV = (BYTE*)calloc(DEFAULT_IV_SIZE + 1, sizeof(BYTE));
char* ciphertext= 0;
DWORD dwPlainSize = lstrlenA(*plaintext), dwBufSize = 0;
AES256KEYBLOB AESBlob;
memset(&AESBlob, 0, sizeof(AESBlob));
// initalize key and plaintext
StrCpyA((LPSTR)szKey, "00112233445566778899001122334455");
StrCpyA((LPSTR)szIV, "4455667788990011");
// generate key & IV
//if (!CryptGenRandom(hProv, DEFAULT_AES_KEY_SIZE, szKey)) {goto error;}
//if (!CryptGenRandom(hProv, DEFAULT_IV_SIZE, szIV)) {goto error;}
// blob data for CryptImportKey() function (include key and version and so on...)
AESBlob.bhHdr.bType = PLAINTEXTKEYBLOB;
AESBlob.bhHdr.bVersion = CUR_BLOB_VERSION;
AESBlob.bhHdr.reserved = 0;
AESBlob.bhHdr.aiKeyAlg = CALG_AES_256;
AESBlob.dwKeySize = DEFAULT_AES_KEY_SIZE;
StrCpyA((LPSTR)AESBlob.szBytes, (LPCSTR)szKey);
// create a cryptographic service provider (CSP)
if (!CryptAcquireContextA(&hProv, NULL, MS_ENH_RSA_AES_PROV_A, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {goto error;}
// populate crypto provider (CSP)
if (!CryptImportKey(hProv, (BYTE*)&AESBlob, sizeof(AES256KEYBLOB), NULL, CRYPT_EXPORTABLE, &hKey)) { goto error; }
if (!CryptSetKeyParam(hKey, KP_IV, szIV, 0)) { goto error; }
// ciphertext allocation
dwBufSize = BUFFER_FOR_PLAINTEXT + dwPlainSize;
ciphertext = (char*)calloc(dwBufSize, sizeof(char));
memcpy_s(ciphertext, dwBufSize, *plaintext, dwPlainSize);
// encryption
if (!CryptEncrypt(hKey, NULL, TRUE, 0, (BYTE*)ciphertext, &dwPlainSize, dwBufSize) ) { goto error; }
The key and IV are given to OpenSSL as hexadecimal. There's no need to generate visible ASCII characters only. You could have figured that out yourself just by pretending that the generated key and IV was "Hello". You get an error message that's pretty helpful.
$> openssl aes-256-cbc -d -K Hello -iv Hello
hex string is too short, padding with zero bytes to length
non-hex digit
invalid hex iv value
And hey, look at your code,
// initalize key and plaintext
StrCpyA((LPSTR)szKey, "00112233445566778899001122334455");
StrCpyA((LPSTR)szIV, "4455667788990011");
These string are not only ASCII text, they happen to be hex as well. It looks to as if you just need to convert hex to bytes in your code or convert bytes to a hex string if you have the bytes in code and want to generate the command line.

Encrypt file with AES 128 mode cbc using public key X509 V3 (PKCS7)

I need to encrypt file with AES 128 mode cbc.
The key of AES need to encrypt using public key X509 V3.
All this need to save in binary file PKCS7.
BIO* certBIO = BIO_new_mem_buf((void*)&certData[0], certData.size());
if (certBIO)
x509 = d2i_X509_bio(certBIO, 0);
BIO_free(certBIO);
sk_X509_push(x509_stack, x509);
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, &inData[0], inData.size());
BIO_flush(bio);
PKCS7* pkcs7_encrypt = PKCS7_encrypt(x509_stack, bio, EVP_aes_128_cbc(), PKCS7_BINARY);
FILE *fpPKCS7 = fopen(szPKCS7File, "wb");
if (!fpPKCS7)
return 1;
i2d_PKCS7_fp(fpPKCS7, pkcs7_encrypt);
fclose(fpPKCS7);
X509_free(x509);
sk_X509_pop_free(x509_stack, X509_free);
Is it correct code ?
The function i2d_PKCS7_fp crashes.
I found solution:
PKCS7* pkcs7_encrypt = PKCS7_encrypt(x509_stack, bioIn, EVP_aes_128_cbc(), PKCS7_BINARY);
BIO *bioOut = BIO_new(BIO_s_file());
BIO_write_filename(bioOut, szPKCS7File);
i2d_PKCS7_bio(bioOut, pkcs7_encrypt);
But i have a question else.
What information really in this file ?
Is it contains the encrypted key of AES by public key of certificat ?

Crash in d2i_RSAPrivateKey_fp()

I have a small VC++ code to load an ASN.1 .der file and read its private key. It compiles and runs fine, but it crashes at function d2i_RSAPrivateKey_fp.
Tools Used:
Visual Studio 2008 [V90]
OpenSSL 1.0.2d 9 Jul 2015
I have also tested the certificate to check if it was corrupted
Testing certificate:
C:\OpenSSL-Win32\bin>openssl rsa -in private.der -check -inform DER
RSA key ok
writing RSA key
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQC/4V4jxRYeFBDh8XBNq2EMs3hXWW5IIN51lM/Oyz0U/Bw8HF0m
/VBJU3SCy2FzoYPa2o3HHYWDMnjmOlXb9aXR3hyLHnvgvE/0YkMXlxh58H1srjw4
FL7cLXe+lwFbZYtxRaHyn/3U3NIkZkCzR74oxHwyWJ1/zz+TzpmNx8AfyQIDAQAB
AoGAMn+9puxXxdLCHrTMOaTBBfa11UdUHueHpKplhqc2jC9NvwQ3/+rrFmFAaKve
GfCIIzEh3yWF3eGKsAzqS9l6qiyAT+LaCPcn7FbAsaMdVGPc4G290/3maASjQMj6
YfNTcmvPaqfq2+B+aNunS0pi6XGv3917KdGo4hTa2xFXwAECQQDfm5Rh0CjDwhjh
urghbvBgGVRJiAFowgp9xdJj8Hm/U+zXK9Tz2SwVcbEKbcqSVZqB8Keb11TEfF3W
JGIHGdOJAkEA260wKNAdWxgEXiSCEx/tSV3bcRQg4ypTqODc0cWtM295/lhV2Hgx
3zWQ5NiBYZsU104DLWlxnqulJKyBECmaQQJAFAjskpcEEAYkFJWWSeiWwQWD99Kn
zasVJY/D+hBh2DK81cqnmfGrcYBuTHDp5ZKl9V6Kpfv1LGW4Qqef4OL/gQJAb1Mp
IMW22r8lF4Bw2rhHS/LgjkGhGP4OP3sU7Mm8qGBJ9ndVqcnfnDpNH2wIxSoOOb4z
JgRVrA9YNWmmTkaHAQJBANiY3RDyEouYoNKN0oWXsZ/N6BwSFOYAAknmdY5uRwu2
tUQHFiW7u2wZCFmIZVqSBZc5XBdZcmffZeEiO1nVYH4=
-----END RSA PRIVATE KEY-----
C:\OpenSSL-Win32\bin>
Below is the code for the project.
Thing I have tried:
Win32OpenSSL-0_9_8zg (but same issue), OpenSSL 1.0.1
Tried linking libeay to MD, MDd, MT
Set flags to Generate /MAP, /MAPINFO:EXPORTS ,/ASSEMBLYDEBUG
exported to VS2010 project and compiled with openssl32 for VS2010
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include <stdio.h>
int main()
{
int ret;
RSA *pkey=NULL;//RSA_new();
RSA *rsa =NULL;//RSA_new();
unsigned char text[2048/8] = "this is a test string";
unsigned char encrypted[4098]={};
unsigned char decrypted[4098]={};
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
ERR_load_ERR_strings();
FILE *fp;
fp = fopen("C:/Users/Public/private.der","rb");
if (fp != NULL)
{
rsa = d2i_RSAPrivateKey_fp(fp, &pkey); // <<< CRASH
}
else
{
//return "Error::Unable to read private key file";
}
if (rsa)
{
// RSA is good
ret = RSA_private_encrypt(RSA_size(pkey), text, encrypted, pkey, RSA_NO_PADDING);
if (ret == -1)
{
fclose(fp);
//return "Error::Failed to encrypt the data";
}
}
else
{
fclose(fp);
//return "Error::Failed to get *RSA Handle1";
}
fclose(fp);
}
Second attempt
I am facing issue with importing private key from ASN.1 .der file to a .pem file.
C:\OpenSSL-Win32\bin>openssl rsa -in private.der -inform DER -out privatepem.pem -outform PEM
writing RSA key
Now, I have .PEM file which have private key in Base64 Form which i will convert it to RSA *rsa; structure.
int main()
{
int ret;
RSA *pkey=NULL;//RSA_new();
RSA *rsa =NULL;//RSA_new();
FILE *fp;
fp = fopen("C:/Users/Public/privatepem.pem","r");
if(PEM_read_RSAPrivateKey(fp, &rsa, NULL, NULL) == NULL) // <<<CRASH
{
printf("\n%s\n", "Error Reading public key");
}
else
{
printf("\n%s\n", "Private key Imported");
}
return 0;
}
But the new code operating on .pem files has a crash while copying to RSA structure at PEM_read_RSAPrivateKey.
Why am I getting the crash? Is there something I am missing to do, may be initialization of some sort or something wrong with the key?
See the warnings at d2i_X509. This code will attempt to free (or reuse parts of) the pkey object:
if (fp != NULL)
rsa = d2i_RSAPrivateKey_fp(fp, &pkey); /* CRASH */
OpenSSL is not exactly known for validating parameters before taking actions on them :)
Instead, use:
if (fp != NULL)
rsa = d2i_RSAPrivateKey_fp(fp, NULL);
Or:
RSA* pkey = RSA_new();
...
if (fp != NULL)
rsa = d2i_RSAPrivateKey_fp(fp, &pkey);
Also see Seg fault from d2i_RSAPrivateKey_fp on the OpenSSL Users mailing list.
As for the crash after using the API as directed, then it sounds like you have other issues. Since OpenSSL is cross-platofrm, its "write once, run everywhere". Port the OpenSSL-based TLS logic to Linux, and get a memory checker like Valgrind on it.
Problem was solved by replacing the OpenSSL binaries.
Code worked without any changes.
Project had prebuild dependencies of OpenSSL which was from unknown source.
So just by replacing dependencies from "https://slproweb.com/products/Win32OpenSSL.html" it worked like a charm.
:)
Thanks guys

PEM_read_bio_RSAPrivateKey() difference on Windows

I am writing some code to RSA sign data. I have gotten this code to work successfully on Ubuntu (OpenSSL version is 1.0.1f-1ubuntu2.7). The code takes the message, and produces the signed result with the private key, I can verify is correct using a separate program and the matching public key.
Under windows, the exact same code produces a different result, the signed data is different (And does not verify. I can't figure out why. I am using the OpenSSL distribution 'Win64OpenSSL-1_0_1j' from http://slproweb.com/products/Win32OpenSSL.html .
Here is the code:
const char * rsa_pri_key = "-----BEGIN RSA PRIVATE KEY-----\n"
"MII...L1t\n"
"yC0...+zk\n"
"c0...5Q\n"
"-----END RSA PRIVATE KEY-----\n";
BIO* bio = BIO_new_mem_buf( (void*)rsa_pri_key, -1 );
BIO_set_flags( bio, BIO_FLAGS_BASE64_NO_NL );
RSA* private_key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL ) ;
if( !private_key )
{
std::cout << "Key load failed" << std::endl;
}
BIO_free( bio ) ;
// Sign the data
signature = (unsigned char*) malloc(RSA_size(private_key));
RSA_sign(NID_md5, (unsigned char*) message, strlen(message), signature, &slen, private_key)
I think the issue might be related to the char type on Linux and windows differing in whether they're signed or not. You're casting to (unsigned char *) which might be where the issue arises.

How to use X509_verify()

How can we use X509_verify(). I have two certificates. The first certificate is the Root Certificate which signed the next certificate (which is my Certificate). So I want to check if my certificate is signed by the root certificate using x509_verify() in C++. My goal is to keep the code simple and Understandable so I can also put it online.
Signature of X509_verify is
int X509_verify(X509 * x509, EVP_PKEY * pkey);
Suppose of you have root certificate in root and your certificate in mycert;
X509 * root;
X509 * mycert;
//Get root certificate into root
//Get mycert into mycert.
//Get the public key.
EVP_PKEY * pubkey = X509_get_pubkey(root);
//verify. result less than or 0 means not verified or some error.
int result = X509_verify(mycert, pubkey);
//free the public key.
EVP_PKEY_free(pubkey);
I think this would help you.
I think dbasic and Balamurugan answered how to use it. Here's how to interpret the errors you get from it. I find error handling is much more important than business logic because nearly anyone can copy/paste code that works in a benign environment. How you respond to failures, broken/bad inputs and a hostile environments matter more.
The source code for the function is in <openssl dir>/crypto/x509/x_all.c:
int X509_verify(X509 *a, EVP_PKEY *r)
{
return(ASN1_item_verify(ASN1_ITEM_rptr(X509_CINF),a->sig_alg,
a->signature,a->cert_info,r));
}
ASN1_item_verify id defined in <openssl dir>/crypto/asn1/a_verify.c:
int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *alg,
ASN1_BIT_STRING *signature, void *asn, EVP_PKEY *pkey)
{
...
}
The function returns -1 on failure with one of the following error codes:
ERR_R_PASSED_NULL_PARAMETER if pkey is NULL
ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM if alg is unknown using an OID lookup
ASN1_R_WRONG_PUBLIC_KEY_TYPE if the pkey type does not match the pkey->method
ERR_R_MALLOC_FAILURE if a buffer allocation fails
The function returns 0 on failure with one of the following error codes:
ERR_R_EVP_LIB if EVP_DigestVerifyInit fails
ERR_R_EVP_LIB if EVP_DigestVerifyUpdate fails
ERR_R_EVP_LIB if EVP_DigestVerifyFinal fails
On success, the function returns 1 (from around line 220):
...
if (EVP_DigestVerifyFinal(&ctx,signature->data,
(size_t)signature->length) <= 0)
{
ASN1err(ASN1_F_ASN1_ITEM_VERIFY,ERR_R_EVP_LIB);
ret=0;
goto err;
}
ret=1;
err:
EVP_MD_CTX_cleanup(&ctx);
return(ret);
} /* End of function */
From <openssl dir>/crypto/err/err.h, you use ERR_get_error() to retrieve the error code:
err.h:#define ASN1err(f,r) ERR_PUT_error(ERR_LIB_ASN1,(f),(r),__FILE__,__LINE__)
Step1 : Read the certificate and convert the Certificate into X509 structure
// the below will show how to read the certificate from the file (DER or PEM Encoded)
X509* oCertificate=NULL;
FILE *lFp=NULL;
lFp=fopen(iFilePath.c_str(),"rb"); // iFilepath is the string
if(lFp==NULL)
{
oCertificate=NULL;
}
else
{
oCertificate = PEM_read_X509(lFp, NULL, NULL, NULL);
if (oCertificate == NULL )
{
//Certificate may be DER encode
oCertificate = d2i_X509_fp(lFp, NULL);
}
fclose(lFp);
}
// OCertificate contains
Step 2: now read the Root certificate key
(Note check the X509 is NULL or not before use)
Step 3 : use the X509_verify() function.