BIO to EVP_PKEY (Public and Private) - c++

I am stuck on how to convert a BIO* to its EVP_PKEY counterparts so that i can later use it in encryption. Currently my keygen routine looks like this:
bool SecureCrypto::GenerateRSAKeys()
{
BIGNUM* BigNum = nullptr;
BIGNUM* BigNum2 = nullptr;
BigNum = BN_new();
if (BN_set_word(BigNum, RSA_F4) <= 0)
return false;
BigNum2 = BN_new();
if (BN_set_word(BigNum2, RSA_F4) <= 0)
return false;
m_ServerKeyPair = RSA_new();
m_ClientKeyPair = RSA_new();
RSA_generate_key_ex(m_ServerKeyPair,RSA_KeyLen, BigNum, NULL);
RSA_generate_key_ex(m_ClientKeyPair,RSA_KeyLen, BigNum2, NULL);
PEM_write_bio_RSAPublicKey(m_ServerPublicKeyBIO, m_ServerKeyPair);
PEM_write_bio_RSAPrivateKey(m_ServerPrivateKeyBIO,
m_ServerKeyPair,EVP_aes_256_cbc(), (unsigned char*)RSA_PrivKeyPassPhrase.c_str(),
RSA_PrivKeyPassPhrase.size(), NULL, NULL);
PEM_write_bio_RSAPublicKey(m_ClientPublicBIO, m_ClientKeyPair);
PEM_write_bio_RSAPrivateKey(m_ClientPrivateBIO, m_ClientKeyPair, EVP_aes_256_cbc(),
(unsigned char*)RSA_PrivKeyPassPhrase.c_str(), RSA_PrivKeyPassPhrase.size(), NULL,
NULL);
BN_free(BigNum);
BN_free(BigNum2);
return true;
}
Later on i need to convert m_ServerPrivateKeyBIO into a EVP_PKEY for use with EVP_SealInit.
I only know of PEM_read_bio_PrivateKey(), but everytime i call it i get a return value of 0 (fail).I call it as such:
PEM_read_bio_PrivateKey(m_ServerPrivateKeyBIO, NULL, NULL, "Enc Key");
I plan on the server doing encryption with its private key then sending data to client and client decrypting it with the servers public key, i would appreciate any help here, as the OpenSSL docs don't help at all.
P.S i have client and server keys being generated for a test, obviously this would not happen in final code.

Related

How to store a private key and a certificate in a x509_STORE_CTX

I need a x509_STORE_CTX for Unreal engine to add the certificate to the SSL configuration. Right now I have this code
FILE* file = fopen(pathToPrivKey, "rb");
EVP_PKEY* privKey = PEM_read_PrivateKey(file, NULL, NULL, NULL);
fclose(file);
file = fopen(pathToCert, "rb");
X509* x509 = PEM_read_X509(file, NULL, NULL, NULL);
fclose(file);
X509_STORE* store = X509_STORE_new();
X509_STORE_CTX* ctx = X509_STORE_CTX_new();
X509_STORE_add_cert(store, x509);
X509_STORE_CTX_init(ctx, store, NULL, NULL);
int rc = X509_verify_cert(ctx);
int err = X509_STORE_CTX_get_error(ctx);
Having this, the results for rc and err are -1 and 69 respectively, so there is something wrong but I don't know what (I must say that I am pretty new to OpenSSL and what I did might not make any sense). I have tried before to validate the private key and got a good result, so I think that there might be something wrong in the way I am doing the rest.

Straight forward example using CryptoAPI Next Generation (CNG) to encrypt data

I'd like to implement data encryption and decryption in a C++ application running on Windows. I've spent considerable time looking around the Web and am thinking I should probably use the Windows Cryptography API: Next Generation (CNG) functions (although I'm open to better alternatives).
What I find everywhere are complex examples that do all sorts of stuff. I don't feel that confident in this area and so I'd like to find a simple example. In the end, I need a method that takes a string and encrypts, and another methods that decrypts the data back to the string. The user would supply a password for both operations.
This must have been done countless times already. Can anyone point me to a complete and competent example? Ultimately, I'll end up with an Encrypt() and Decrypt() method.
Something that is both secure and performant would be ideal.
Before encrypting (and decrypting) you need to derive key from password with key derivation functions (for example PBKDF2 with SHA256). To prevent pre-computed dictionary attacks
in additional to password you will also need random string (called salt).
Next pick cipher algorithm (AES with 256-bit key is good one) and cipher mode (ECB cipher mode considered weak, so use any other for example CBC). Also it will require one more random string (called initialization vector).
So encrypting algorithm will be:
Generate random salt
Derive key(password, salt) = key
Generate random IV
Encrypt(key, IV, plain text) = cipher text
Input parameters: plain text, password
Output parameters: cipher text, salt, IV
Decrypting algorithm will be:
Derive key(password, salt) = key
Decrypt(key, iv, cipher text) = plain text
Input parameters: cipher text, salt, iv, password
Output parameters: plain text
Sample code:
#include <Windows.h>
#include <iostream>
#include <vector>
#include <array>
#pragma comment(lib, "bcrypt")
static NTSTATUS gen_random(BYTE* buf, ULONG buf_len)
{
BCRYPT_ALG_HANDLE hAlg = nullptr;
NTSTATUS status = NTE_FAIL;
do {
status = BCryptOpenAlgorithmProvider(&hAlg, L"RNG", nullptr, 0);
if (status != ERROR_SUCCESS) {
return status;
}
status = BCryptGenRandom(hAlg, buf, buf_len, 0);
} while (0);
if (hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
}
return status;
}
static NTSTATUS derive_key(BYTE* pass, ULONG pass_len, BYTE* salt,
ULONG salt_len, const ULONG iteration, BYTE* derived_key, ULONG derived_key_len)
{
BCRYPT_ALG_HANDLE hPrf = nullptr;
NTSTATUS status = ERROR_SUCCESS;
do {
status = BCryptOpenAlgorithmProvider(&hPrf, L"SHA256", nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG);
if (status != ERROR_SUCCESS) {
break;
}
status = BCryptDeriveKeyPBKDF2(hPrf, pass, pass_len, salt, salt_len, iteration, derived_key, derived_key_len, 0);
} while (0);
if (hPrf) {
BCryptCloseAlgorithmProvider(hPrf, 0);
}
return status;
}
static NTSTATUS do_encrypt(BYTE* key, ULONG key_len, BYTE* plain_text, ULONG plain_text_len,
std::vector<BYTE>& iv, std::vector<BYTE>& cipher_text)
{
NTSTATUS status = NTE_FAIL;
BCRYPT_ALG_HANDLE hAlg = nullptr;
BCRYPT_KEY_HANDLE hKey = nullptr;
do {
status = BCryptOpenAlgorithmProvider(&hAlg, L"AES", nullptr, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* create key object */
status = BCryptGenerateSymmetricKey(hAlg, &hKey, nullptr, 0, key, key_len, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* set chaining mode */
std::wstring mode = BCRYPT_CHAIN_MODE_CBC;
BYTE* ptr = reinterpret_cast<BYTE*>(const_cast<wchar_t*>(mode.data()));
ULONG size = static_cast<ULONG>(sizeof(wchar_t) * (mode.size() + 1));
status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, ptr, size, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* generate iv */
ULONG block_len = 0;
ULONG res = 0;
status = BCryptGetProperty(hAlg, BCRYPT_BLOCK_LENGTH, reinterpret_cast<BYTE*>(&block_len), sizeof(block_len), &res, 0);
if (status != ERROR_SUCCESS) {
break;
}
iv.resize(block_len);
status = gen_random(iv.data(), static_cast<ULONG>(iv.size()));
if (status != ERROR_SUCCESS) {
break;
}
/* BCryptEncrypt modify iv parameter, so we need to make copy */
std::vector<BYTE> iv_copy = iv;
/* get cipher text length */
ULONG cipher_text_len = 0;
status = BCryptEncrypt(hKey, plain_text, plain_text_len, nullptr, iv_copy.data(), static_cast<ULONG>(iv_copy.size()),
nullptr, cipher_text_len, &cipher_text_len, BCRYPT_BLOCK_PADDING);
if (status != ERROR_SUCCESS) {
break;
}
cipher_text.resize(static_cast<size_t>(cipher_text_len));
/* now encrypt */
status = BCryptEncrypt(hKey, plain_text, plain_text_len, nullptr, iv_copy.data(), static_cast<ULONG>(iv_copy.size()),
cipher_text.data(), cipher_text_len, &cipher_text_len, BCRYPT_BLOCK_PADDING);
} while (0);
/* cleanup */
if (hKey) {
BCryptDestroyKey(hKey);
}
if (hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
}
return status;
}
static NTSTATUS do_decrypt(BYTE* key, ULONG key_len, BYTE* cipher_text, ULONG cipher_text_len,
const std::vector<BYTE>& iv, std::vector<BYTE>& plain_text)
{
NTSTATUS status = NTE_FAIL;
BCRYPT_ALG_HANDLE hAlg = nullptr;
BCRYPT_KEY_HANDLE hKey = nullptr;
do {
status = BCryptOpenAlgorithmProvider(&hAlg, L"AES", nullptr, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* create key object */
status = BCryptGenerateSymmetricKey(hAlg, &hKey, nullptr, 0, key, key_len, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* set chaining mode */
std::wstring mode = BCRYPT_CHAIN_MODE_CBC;
BYTE* ptr = reinterpret_cast<BYTE*>(const_cast<wchar_t*>(mode.data()));
ULONG size = static_cast<ULONG>(sizeof(wchar_t) * (mode.size() + 1));
status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, ptr, size, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* BCryptEncrypt modify iv parameter, so we need to make copy */
std::vector<BYTE> iv_copy = iv;
/* get expected plain text length */
ULONG plain_text_len = 0;
status = BCryptDecrypt(hKey, cipher_text, cipher_text_len, nullptr, iv_copy.data(), static_cast<ULONG>(iv_copy.size()),
nullptr, plain_text_len, &plain_text_len, BCRYPT_BLOCK_PADDING);
plain_text.resize(static_cast<size_t>(plain_text_len));
/* decrypt */
status = BCryptDecrypt(hKey, cipher_text, cipher_text_len, nullptr, iv_copy.data(), static_cast<ULONG>(iv_copy.size()),
plain_text.data(), plain_text_len, &plain_text_len, BCRYPT_BLOCK_PADDING);
/* actualize size */
plain_text.resize(static_cast<size_t>(plain_text_len));
} while (0);
/* cleanup */
if (hKey) {
BCryptDestroyKey(hKey);
}
if (hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
}
return status;
}
NTSTATUS encrypt(BYTE* pass, ULONG pass_len, const std::vector<BYTE>& plain_text,
std::vector<BYTE>& salt, std::vector<BYTE>& iv, std::vector<BYTE>& cipher_text)
{
NTSTATUS status = NTE_FAIL;
salt.resize(8);
std::array<BYTE, 32> key{0x00};
do {
/* generate salt */
status = gen_random(salt.data(), static_cast<ULONG>(salt.size()));
if (status != ERROR_SUCCESS) {
break;
}
/* derive key from password using SHA256 algorithm and 20000 iteration */
status = derive_key(pass, pass_len, salt.data(), static_cast<ULONG>(salt.size()), 20000, key.data(), key.size());
if (status != ERROR_SUCCESS) {
break;
}
/* encrypt */
status = do_encrypt(key.data(), static_cast<ULONG>(key.size()), const_cast<BYTE*>(plain_text.data()),
static_cast<ULONG>(plain_text.size()), iv, cipher_text);
} while (0);
SecureZeroMemory(key.data(), key.size());
return status;
}
NTSTATUS decrypt(BYTE* pass, ULONG pass_len, const std::vector<BYTE>& salt, const std::vector<BYTE>& iv,
const std::vector<BYTE>& cipher_text, std::vector<BYTE>& plain_text)
{
NTSTATUS status = NTE_FAIL;
std::array<BYTE, 32> key{0x00};
do {
/* derive key from password using same algorithm, salt and iteraion count */
status = derive_key(pass, pass_len, const_cast<BYTE*>(salt.data()), static_cast<ULONG>(salt.size()),
20000, key.data(), key.size());
if (status != ERROR_SUCCESS) {
break;
}
/* decrypt */
status = do_decrypt(key.data(), static_cast<ULONG>(key.size()), const_cast<BYTE*>(cipher_text.data()),
static_cast<ULONG>(cipher_text.size()), const_cast<BYTE*>(iv.data()),
static_cast<ULONG>(iv.size()), plain_text);
} while (0);
SecureZeroMemory(key.data(), key.size());
return status;
}
How to use CryptoAPI Next Generation (CNG): Don't use it. Use OTP.
If your user is physically sent the key, then use the simple but unbreakable OTP or One Time Pad (See https://en.wikipedia.org/wiki/One-time_pad).
It is very easy to use.
It is very easy to understand.
Simply supply enough OTP keys to the user, which you or someone randomly types in via a keyboard (I told you it is easy), to last for what you think is a reasonable use by the user.
Do NOT derive the keys from passwords or from anything else that the previous posters listed. That is lazy and is NOT secure. Make the keys as I said and then they will NOT be linked to any code source at all.
Do NOT waste your time with "learn some of the fundamentals of encryption and cryptography" (if that learning is not directly in support of your using One Time Pads).
For you to read some discussions and definitions related to OTP's:
https://www.slideshare.net/AsadAli108/3-l4
https://www.slideshare.net/Jonlitan/one-time-pad-encryption-technique
There is no commonly known encryption that even comes close to OTPs. Example: OTPs can not be mathematically or computationally broken down into a mathematical or computational process.
I think that you have asked for simple, and easy, and not so complicated, but secure, and One Time Pads are all that. If you supply the keys to the user directly, then no human (other than Christians with enough faith) can break them.
Some links to help you with hopefully useful independent examples in C and C++:
A quick search gave me these examples which due to the limit on time for this bounty I have not tested:
https://www.sanfoundry.com/cpp-program-implement-one-time-pad-algorithm/
and
http://www.cplusplus.com/forum/beginner/179981/
and
https://github.com/DDomjosa/One-time-pad-encryption/blob/master/One%20time%20pad%20encryption.cpp
ps: In case you are wondering why I post but do not reply to comments: My browser does not seem to support the "add a comment" on these Stack Overflow pages, so I can post but I cannot (until SO makes their pages backward compatible sufficient for me) reply or comment beyond that.

AES/Rijndael in C++ Microsoft CryptoAPI

How can I convert the following cryptography code (VB.NET 4.0) to the C++ equivalent one, using Microsoft CryptoAPI (CryptDeriveKey, BCrypt[...] functions, CryptAcquireContext, etc.)? (I haven't found a single article on the Internet describing AES using Microsoft CryptoAPI...)
Dim Key(31) As Byte
Dim IV(15) As Byte
Array.Copy(SomeByteArray, IV, 16)
Array.Copy((New SHA512Managed).ComputeHash(SomeByteArray), Key, 32)
Using AESEncr As New RijndaelManaged() With {.Padding = PaddingMode.ISO10126}
FinalEncrypted = AESEncr.CreateEncryptor(Key, IV).TransformFinalBlock(AnotherByteArray, 0, AnotherByteArray.GetLength(0))
End Using
and the decrypting one:
Dim Key(31) As Byte
Dim IV(15) As Byte
Array.Copy(SomeByteArray, IV, 16)
Array.Copy((New SHA512Managed).ComputeHash(SomeByteArray), Key, 32)
Using AESEncr As New RijndaelManaged() With {.Padding = PaddingMode.ISO10126}
FinalDecrypted = AESEncr.CreateDecryptor(Key, IV).TransformFinalBlock(FinalEncrypted, 0, FinalEncrypted.GetLength(0))
End Using
(Note: I already have C++ code about the SHA-512 method, so don't bother with that.)
So, the code I made for AES-256 Encryption/Decryption is the following: (it takes BYTE* Data and BYTE* IV as parameters)
BYTE *hash, *res;
HCRYPTPROV hCrypt = NULL;
HCRYPTKEY hKey = NULL;
struct {
BLOBHEADER hdr;
DWORD len;
BYTE key[32];
} key_blob;
key_blob.hdr.bType = PLAINTEXTKEYBLOB;
key_blob.hdr.bVersion = CUR_BLOB_VERSION;
key_blob.hdr.reserved = 0;
key_blob.hdr.aiKeyAlg = CALG_AES_256;
key_blob.len = 32;
hash = ComputeSHA512Hash(IV);
copy(hash, hash + 32, key_blob.key);
res = new BYTE[16];
copy(Data, Data + 15, res);
res[15] = 0;
// Get the Microsoft Enhanced RSA and AES Cryptographic Service Provider
if (!CryptAcquireContext(&hCrypt, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0))
throw E_FAIL;
// Import our key blob
if (!CryptImportKey(hCrypt, (BYTE *)&key_blob, sizeof(key_blob), NULL, 0, &hKey))
throw E_FAIL;
// Set the mode to Cipher Block Chaining
DWORD dwMode = CRYPT_MODE_CBC;
if (!CryptSetKeyParam(hKey, KP_MODE, (BYTE *)&dwMode, 0))
throw E_FAIL;
// Set the Initialization Vector to ours
if (!CryptSetKeyParam(hKey, KP_IV, IV, 0))
throw E_FAIL;
// Do the main encryption
DWORD pdwDataLen = 15;
if (!CryptEncrypt(hKey, NULL, TRUE, 0, res, &pdwDataLen, 16))
throw E_FAIL;
// Do the main decryption
pdwDataLen = 16;
if (!CryptDecrypt(hKey, NULL, TRUE, 0, res, &pdwDataLen))
throw E_FAIL;
// Destroy whatever was created before (free memory)
delete hash;
delete res;
if (hKey)
CryptDestroyKey(hKey);
if (hCrypt)
CryptReleaseContext(hCrypt, 0);
As I previously said, I already have code for the ComputeSHA512Hash() function, so my code is complete for my purposes. I hope this code will be useful for everyone wanting to write AES-256 code.

Converting a SSL Cert string into a valid X509_STORE_CTX

I'm trying to get both of these certs into a X509_STORE_CTX, but when I go to read them out, they are both NULL. Any ideas?
The certs look like:
// Not the real certs. Just trying to illustrate that the certs are just a new line
// delimited string
const char *certA = "-----BEGIN CERTIFICATE-----\nMIIGWDCCBUCgAwI......\n.....\n"
SSL_library_init();
SSL_CTX * sslCtx = SSL_CTX_new(SSLv23_client_method());
X509_STORE *store = SSL_CTX_get_cert_store(sslCtx);
X509_STORE_CTX *store_ctx = X509_STORE_CTX_new();
BIO *bio;
X509 *certificate;
/*First cert*/
bio = BIO_new(BIO_s_mem());
BIO_write(bio,(const void*)certA ,sizeof(certA));
certificate = PEM_read_bio_X509(bio, NULL, NULL, NULL);
X509_STORE_add_cert(store, certificate);
/*second cert*/
bio = BIO_new(BIO_s_mem());
BIO_write(bio,(const void*)certB ,sizeof(certB));
certificate = PEM_read_bio_X509(bio, NULL, NULL, NULL);
X509_STORE_add_cert(store, certificate);
X509_STORE_CTX_init(store_ctx, store, NULL, NULL);
sizeof(certA) here will provide just the size of that const char* variable, which is the size of a pointer (mostly 4 or 8).
Try declaring the certificate contents as static const char certA[] instead.
Also using BIO_puts() and avoiding the sizeof() completely might be easier.

Problems to import RSA key in other computer

I am creating a little tool for encrypt and decrypt using a pair keys (public and private keys).
I export public and private key on my computer and I can encrypt and decrypt files without problems. I have problem when I try decrypt files in other machine with the same public key.
// initializing CSP HCRYPTPROV hProv; HCRYPTKEY hKey;
if(!CryptAcquireContext(hProv, NULL, NULL, PROV_RSA_FULL, 0)){ if(GetLastError() == NTE_BAD_KEYSET){ if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)){ return FALSE; } } }
// create a pair keys if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_ARCHIVABLE, &hKey)) return FALSE;
// public key if (!CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, NULL, sizePublicKey)) return FALSE;
*publicKey = (BYTE *) LocalAlloc(LPTR, *sizePublicKey * sizeof(DWORD)); if(*publicKey == NULL) return FALSE;
if (!CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, *publicKey, sizePublicKey)) return FALSE; // save public key on file
// private key if (!CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, NULL, sizePrivateKey)) return FALSE;
*privateKey = (BYTE *) LocalAlloc(LPTR, *sizePrivateKey * sizeof(DWORD)); if(*publicKey == NULL) return FALSE;
if (!CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, *privateKey, sizePrivateKey)) return FALSE;
PrivateKey.key = (BYTE *) LocalAlloc(LPTR, *sizePrivateKey * sizeof(DWORD)); if(*publicKey == NULL) return FALSE; // save private key on file
//I encrypt file using if(!CryptEncrypt(hKey, 0, TRUE, 0, cache, &sizeCache, BLOCK_SIZE_ENCRYPT)){
free(cache);
return FALSE; }
//To decrypt file //First import public key
CryptImportKey(hProv, publicKey, sizePublicKey, 0, 0, &hKey)
//To decrypt: if (!CryptDecrypt(hKey, 0, TRUE, 0, cache, &sizeCache)){
free(cache);
return FALSE; }
In the same computer that key ware created the application encrypt and decrypt correctly but if I try decrypt files in other computer the CryptDecrypt() failed with error 80090003 (error got by GetLastError())
Any idea? what am I doing wrong...?
How to I can export the public key to other computer?
Thanks!
Probably you are not exporting the key, just using the CSP containing the key, while you are in the same computer, the key is stored in the container where you "link" by using the cryptoapi. Once you go to other computer the container is not present, so you can not use the key.
Make sure that the Private Key is exportable.