Separating public and private keys from RSA keypair variable - c++

As the title says, I have some code that generates a pair of RSA keys. I want to split them apart and use them individually to encrypt/decrypt, rather than use the variable "keypair" to encrypt, and decrypt.
I am working to transfer data across a network, and want to encrypt it using simple RSA encryption. Therefore i want to send the public key over to a different user, so he can use it to encrypt some data, and then send it back to me.
Here is the code that generates the keys:
//Generate key pair
RSA *keypair = RSA_generate_key(KEY_LENGTH, PUB_EXP, NULL, NULL);
I now want to separate the public from the private key, so i can use them independently to encrypt and decrypt data. How can i do that?
I've got some code that takes the "keypair" and extracts some information into some "BIO" variables, although i am not sure how that would help me:
// 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);
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';
#ifdef PRINT_KEYS
printf("\n%s\n%s\n", pri_key, pub_key);
#endif
printf("done.\n");
This code works, since i've tested it in visual studio 2012.
Any ideas on how to separate the keys, then maybe put them back together in a "keypair" or maybe how to use them separately to encrypt/decrypt some string variables?
Thank you
(FULL CODE)
#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() {
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
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);
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';
#ifdef PRINT_KEYS
printf("\n%s\n%s\n", pri_key, pub_key);
#endif
printf("done.\n");
// Get the message to encrypt
printf("Message to encrypt: ");
fgets(msg, KEY_LENGTH-1, stdin);
msg[strlen(msg)-1] = '\0';
// Encrypt the message
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, keypair, 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;
}
// Decrypt it
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);
goto free_stuff;
}
printf("Decrypted message: %s\n", decrypt);
getchar();
//printf("%s", pub_key);
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);
}
Found this code here : https://shanetully.com/2012/04/simple-public-key-encryption-with-rsa-and-openssl/

I want to split [the keypair] apart and use them individually to encrypt/decrypt
... Any ideas on how to separate the keys, then maybe put them back together in a "keypair" or maybe how to use them separately to encrypt/decrypt some string variables?
You can use RSAPublicKey_dup and RSAPrivateKey_dup, without the need to round trip them by ASN.1/DER or PEM encoding them. Here, round tripping consist of using a functions like PEM_write_bio_RSAPublicKey and PEM_read_bio_RSAPublicKey.
Below is a sample program that uses them. Its written in C++ (thanks for adding that tag).
While you can separate them, they both use the RSA structure. The public key has some members set to NULL, like the private exponent.
You can also print the keys with RSA_print, RSA_print_fp and friends. See RSA_print (3) or its use below.
// g++ -Wall -Wextra -std=c++11 -stdlib=libc++ -I/usr/local/ssl/macosx-x64/include \
// t.cpp -o t.exe /usr/local/ssl/macosx-x64/lib/libcrypto.a
#include <memory>
using std::unique_ptr;
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <cassert>
#define ASSERT assert
using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
int main(int argc, char* argv[])
{
int rc;
RSA_ptr rsa(RSA_new(), ::RSA_free);
BN_ptr bn(BN_new(), ::BN_free);
rc = BN_set_word(bn.get(), RSA_F4);
ASSERT(rc == 1);
rc = RSA_generate_key_ex(rsa.get(), 2048, bn.get(), NULL);
ASSERT(rc == 1);
RSA_ptr rsa_pub(RSAPublicKey_dup(rsa.get()), ::RSA_free);
RSA_ptr rsa_priv(RSAPrivateKey_dup(rsa.get()), ::RSA_free);
fprintf(stdout, "\n");
RSA_print_fp(stdout, rsa_pub.get(), 0);
fprintf(stdout, "\n");
RSA_print_fp(stdout, rsa_priv.get(), 0);
return 0;
}
Here is the output:
$ ./t.exe
Public-Key: (2048 bit)
Modulus:
00:aa:5a:cc:30:52:f1:e9:49:3d:a6:25:00:33:29:
a6:fa:f7:53:e0:3c:73:4c:91:41:66:20:ec:62:1f:
27:2a:2a:6c:0f:90:f8:d9:7e:d5:ec:72:7b:38:8c:
ca:12:60:f8:d1:fb:f2:65:7c:b1:3a:b6:4e:26:ba:
5b:86:cc:30:f2:fc:be:c3:a2:00:b9:ea:81:fa:1c:
22:4e:f7:be:a1:1a:66:90:13:b6:12:66:26:23:6d:
22:15:7d:3b:a4:99:44:38:fa:1c:70:63:4e:50:6f:
66:38:6c:f6:1a:13:e1:c7:dc:a6:a1:eb:6f:f9:c9:
59:c8:30:dc:c2:1b:dc:6c:9d:ea:0c:3d:52:5a:00:
ea:c9:c9:85:51:21:9f:ec:95:b3:dc:c2:50:21:29:
c2:64:6c:1e:34:36:d8:61:59:ab:3c:a2:cc:e8:ef:
57:c3:7f:49:86:be:e3:42:88:1b:39:10:b8:2f:fa:
81:ef:a0:94:99:0c:71:ae:1e:82:7f:e3:6e:00:6e:
02:13:66:bb:a9:31:58:ec:90:39:9c:bc:9c:8c:90:
e9:20:f7:20:8e:d6:a3:a3:df:a2:4a:0f:0f:39:b5:
57:b9:ef:6a:27:e0:1a:ed:f6:ce:0d:87:cd:43:03:
bf:67:ef:ff:fd:da:98:cc:22:ab:5e:8d:7b:43:d3:
90:4d
Exponent: 65537 (0x10001)
Private-Key: (2048 bit)
modulus:
00:aa:5a:cc:30:52:f1:e9:49:3d:a6:25:00:33:29:
a6:fa:f7:53:e0:3c:73:4c:91:41:66:20:ec:62:1f:
27:2a:2a:6c:0f:90:f8:d9:7e:d5:ec:72:7b:38:8c:
ca:12:60:f8:d1:fb:f2:65:7c:b1:3a:b6:4e:26:ba:
5b:86:cc:30:f2:fc:be:c3:a2:00:b9:ea:81:fa:1c:
22:4e:f7:be:a1:1a:66:90:13:b6:12:66:26:23:6d:
22:15:7d:3b:a4:99:44:38:fa:1c:70:63:4e:50:6f:
66:38:6c:f6:1a:13:e1:c7:dc:a6:a1:eb:6f:f9:c9:
59:c8:30:dc:c2:1b:dc:6c:9d:ea:0c:3d:52:5a:00:
ea:c9:c9:85:51:21:9f:ec:95:b3:dc:c2:50:21:29:
c2:64:6c:1e:34:36:d8:61:59:ab:3c:a2:cc:e8:ef:
57:c3:7f:49:86:be:e3:42:88:1b:39:10:b8:2f:fa:
81:ef:a0:94:99:0c:71:ae:1e:82:7f:e3:6e:00:6e:
02:13:66:bb:a9:31:58:ec:90:39:9c:bc:9c:8c:90:
e9:20:f7:20:8e:d6:a3:a3:df:a2:4a:0f:0f:39:b5:
57:b9:ef:6a:27:e0:1a:ed:f6:ce:0d:87:cd:43:03:
bf:67:ef:ff:fd:da:98:cc:22:ab:5e:8d:7b:43:d3:
90:4d
publicExponent: 65537 (0x10001)
privateExponent:
66:a4:ce:e3:4f:16:f3:b9:6d:ab:ee:1f:70:b4:68:
28:4f:5d:fa:7e:71:fa:70:8b:37:3e:1f:30:00:15:
59:12:b6:89:aa:90:46:7c:65:e9:52:11:6c:c1:68:
00:2a:ed:c1:98:4d:35:59:2c:70:73:e8:22:ed:a6:
b8:51:d0:2c:98:9d:58:c3:04:2d:01:5f:cf:93:a4:
18:70:ae:2b:e3:fc:68:53:78:21:1d:eb:5c:ed:24:
dc:4d:d8:e2:14:77:46:dd:6c:c5:4b:10:a4:e6:7a:
71:05:36:44:00:36:ca:75:e8:f1:27:2b:11:16:81:
42:5e:2e:a5:c6:a3:c9:cd:60:59:ce:72:71:76:c8:
ca:ba:f0:45:c3:86:07:7b:22:20:c4:74:c6:a8:ab:
7c:2c:f8:de:ea:25:95:81:79:33:54:67:7b:61:91:
80:a8:1f:4c:38:32:d4:4d:2e:a8:7d:9b:d4:1a:3e:
6b:ca:50:3c:a0:61:0e:00:ad:f4:5c:0f:26:1a:59:
00:3c:bd:ee:c3:e8:d0:b8:9b:0e:44:89:49:d1:24:
a4:39:15:dc:0e:c5:d5:41:a2:4a:f4:e5:e3:23:c7:
98:8a:87:f7:18:a6:e2:7b:27:83:f6:fb:62:42:46:
ae:de:ba:48:ad:07:39:40:da:65:17:d1:d2:ed:df:
01
prime1:
00:dd:dc:70:b5:70:ea:10:20:28:40:a0:c3:b8:70:
6d:3d:84:c0:57:2d:69:fc:e9:d4:55:ed:4f:ac:3d:
c2:e9:19:49:f0:ab:c6:bd:99:9e:0f:e5:a4:61:d4:
b3:c5:c2:b1:e4:3a:10:ff:e6:cd:ce:6e:2d:93:bc:
87:12:92:87:7c:d3:dd:bc:32:54:9e:fa:67:b1:9d:
e2:27:53:e6:03:a7:22:17:45:63:0d:42:f3:96:5d:
a3:e0:9c:93:f0:42:8b:bb:95:34:e6:f6:0b:f7:b6:
c5:59:a0:b5:2a:71:59:c0:f2:7e:bf:95:2d:dd:6d:
94:23:2a:95:4a:4f:f1:d0:93
prime2:
00:c4:91:6a:33:1b:db:24:eb:fd:d3:69:e9:3c:e2:
a2:2d:23:7a:92:65:a8:a0:50:1d:0a:2b:b4:f0:64:
e4:40:57:f3:dc:f7:65:18:7d:51:75:73:b9:d6:67:
9b:0e:94:5f:37:02:6c:7f:eb:b9:13:4b:bf:8e:65:
22:0b:2c:c6:8d:2a:a2:88:ec:21:e3:f9:0b:78:b4:
1d:d0:44:e6:36:0d:ec:3c:8f:0a:c7:3b:0d:91:65:
b7:de:a3:c9:a3:2a:8c:7f:1f:a1:d2:6e:9b:ee:23:
78:c1:30:76:87:af:a8:11:a4:15:b4:54:16:d8:94:
71:5c:64:30:43:58:b5:07:9f
exponent1:
2f:91:e8:88:be:e1:30:fb:f4:25:87:52:ef:e5:0b:
47:39:83:94:2d:a4:a0:19:f2:f1:49:a4:df:a5:8e:
79:34:76:ea:27:aa:c1:54:82:d3:9d:c5:95:44:6a:
17:69:1b:83:77:ff:d5:1e:c3:da:13:3d:aa:83:ad:
e2:89:90:8b:6f:52:07:dc:32:d0:b3:98:30:39:4e:
18:68:a0:d4:ff:ad:0b:98:51:18:b2:d6:4f:d3:5c:
23:f8:ee:af:81:55:3c:af:4d:5c:88:3d:20:ac:0b:
bc:9f:fc:b8:50:fd:91:a5:6d:0f:df:08:aa:85:a8:
51:b1:fb:b8:a7:53:8e:09
exponent2:
7d:46:0b:7f:ad:06:19:de:c8:b2:7e:f2:25:5a:6e:
6f:04:08:6e:da:99:00:2a:6e:87:77:d9:65:c7:76:
ec:46:e1:64:f6:ca:18:34:6d:c0:c3:d3:31:00:70:
82:77:2e:c3:59:29:1a:d1:78:ef:02:3c:7f:9c:96:
78:b6:bd:87:64:1f:97:d1:9d:bb:b3:91:8b:08:87:
63:9f:35:74:47:a5:41:e7:0b:c0:73:33:2f:71:bb:
20:0a:14:4c:87:a6:68:b2:19:28:8a:53:98:0e:45:
3c:22:0d:b8:65:cb:60:0a:c9:c6:56:3d:05:24:7d:
a6:9b:37:63:04:5a:c3:13
coefficient:
00:cc:d7:5c:e6:0e:7b:79:d4:cb:4f:6d:82:a7:45:
90:67:90:dc:d3:83:62:f1:4b:17:43:5c:4a:ea:bf:
38:25:c3:6f:34:e2:05:91:5e:60:d6:de:6d:07:1a:
73:71:b3:1d:73:f2:3c:60:ed:ec:42:d4:39:f8:a4:
ae:d5:aa:40:1e:90:b1:eb:b1:05:a3:2f:03:5f:c6:
b7:07:4c:df:0f:c4:a9:80:8c:31:f5:e2:01:00:73:
8a:25:03:84:4e:48:7a:31:8e:e6:b8:04:4c:44:61:
7d:e4:87:1c:57:4f:45:44:33:bb:f3:ae:1c:d2:e1:
99:ed:78:29:76:4d:8c:6d:91
Related, you never ask how to encrypt or decrypt with a RSA key, yet you claim the answer to the encryption and decryption problems is shown in the code in your answer.
You seem to have made the question a moving target :) You should probably avoid that on Stack Overflow (I think its OK to do in those user thread forums). On Stack Overflow, you should ask a separate question.

You can extract the RSA public key from RSA keypair using d2i_RSAPublicKey and i2d_RSAPublicKey (link). Use i2d_RSAPublicKey to encode your keypair to PKCS#1 RSAPublicKey stucture, store it in a bytestring, then use d2i_RSAPublicKey to decode it back to RSA key struct.

I've found a solution to my question among other Stack-Overflow posts and namely Reading Public/Private Key from Memory with OpenSSL
The answer i was 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);
i can send the variable pub across the network, and after it reaches the other side create a RSA variable and put pub inside it:
SOLUTION:
RSA *keypair2 = NULL;
PEM_read_bio_RSAPublicKey( pub, &keypair2, NULL, NULL);
After i've done this i 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);
}
I can then send this encrypt variable back to the first machine, and decrypt it as usual, using my original keypair, without having to send it over the network.
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);
}
Thank you everyone for contributing to this post!!!

Reviewing your code, it appears that you successfully separated the public and private keys into the strings pub_key and pri_key, but then used printf to output them which pasted them back together. To just print the public key change the printf statement to:
printf("\n%s\n", pub_key);

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.

Trouble loading RSA public key from file

I've been using openssl library in my C++ project recently, and I'm facing an issue that i can't fix by myself.
I'm actually trying to load an RSA public key stored in a file and encrypt 64 bytes.
My code works when it uses a public key generated using the function RSA_generate_key, but when i'm using my own public key, it won't work anymore for some reason.
I've suspected key format from pkcs1 pkcs8, tried both PEM_read_RSAPublicKey and PEM_read_RSA_PUBKEY, still not working for some reason...
This is my public key :
-----BEGIN RSA PUBLIC KEY-----
MEYCQQDE91cW7INdIyVon5H/he2b/DIR25wWT0GFLiZOVp0oAgCAVKDvRZ5+Pqu4
f65XbnNUNNHRJLMLEb1t4JgUhgFVAgER
-----END RSA PUBLIC KEY-----
The key from RSA_generate_key function from Openssl library, which is working :
-----BEGIN RSA PUBLIC KEY-----
MEYCQQDsg/4Qm153/Pr8JRruC0SnVvTrWg/lIPheezIpkwVeWjNz9lMDXNUjdK8v
QgfNUCRJYbnxYIeruAdwTzS/bDXbAgER
-----END RSA PUBLIC KEY-----
And here's my code :
RSA.h :
#include <iostream>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <string>
#ifndef RSA_ALGORITHM_H
#define RSA_ALGORITHM_H
#define KEY_LENGTH 512
#define PUBLIC_EXPONENT 17
#define PUBLIC_KEY_PEM 1
#define PRIVATE_KEY_PEM 0
#define LOG(x) \
std::cout << x << std::endl; \
/*
* #brief create_RSA function creates public key and private key file
*
*/
RSA* create_RSA(RSA* keypair, int pem_type, char* file_name);
/*
* #brief public_ecrypt function encrypts data.
* #return If It is fail, return -1
*/
int public_encrypt(int flen, unsigned char* from, unsigned char* to, RSA* key, int padding);
/*
* #brief private_decrypt function decrypt data.
* #return If It is fail, return -1
*/
int private_decrypt(int flen, unsigned char* from, unsigned char* to, RSA* key, int padding);
/*
* #brief create_ecrypted_file function creates .bin file. It contains encrypted data.
*/
void create_encrypted_file(char* encrypted, RSA* key_pair);
#endif //RSA_ALGORITHM_H
RSA.cpp :
#include "RSA.h"
#include <iostream>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <sstream>
#include <iomanip>
int public_encrypt(int flen, unsigned char* from, unsigned char* to, RSA* key, int padding) {
int result = RSA_public_encrypt(flen, from, to, key, padding);
return result;
}
void create_encrypted_file(char* encrypted, RSA* key_pair) {
FILE* encrypted_file = fopen("encrypted_file.bin", "w");
fwrite(encrypted, sizeof(*encrypted), RSA_size(key_pair), encrypted_file);
fclose(encrypted_file);
}
RSA* createRSA(int pem_type, char* file_name) {
RSA* rsa = NULL;
FILE* fp = NULL;
if (pem_type == PUBLIC_KEY_PEM) {
fp = fopen(file_name, "rb");
PEM_read_RSAPublicKey(fp, &rsa, NULL, NULL);
fclose(fp);
}
else if (pem_type == PRIVATE_KEY_PEM) {
fp = fopen(file_name, "rb");
PEM_read_RSAPrivateKey(fp, &rsa, NULL, NULL);
fclose(fp);
}
return rsa;
}
int main() {
LOG("RSA has been started.");
char public_key_pem[11] = "public_key";
RSA* public_key = createRSA(PUBLIC_KEY_PEM, public_key_pem);
LOG("Public key pem file has been created.");;
char message[KEY_LENGTH] = "\xc8\xcd\x21\x74\xb9\x84\x33\xb9\x30\x94\xb3\x60\x26\xde\x12\x5a\x7f\x5e\xd8\x5e\xc2\x7e\xe6\xbb\x9e\x99\x6c\xb3\xb9\x38\xe9\xc6\x23\x8c\xc6\x5d\x36\x15\xfb\x63\x5f\x6f\x08\x0f\x6d\xda\x06\x31\x59\x28\xbc\xae\x4c\xcf\x80\x2f\x96\x80\x54\x7d\xb5\x7b\x82\x83";
char* encrypt = NULL;
LOG(KEY_LENGTH);
LOG(PUBLIC_EXPONENT);
encrypt = (char*)malloc(RSA_size(public_key));
int encrypt_length = public_encrypt(RSA_size(public_key), (unsigned char*)message, (unsigned char*)encrypt, public_key, RSA_NO_PADDING);
if (encrypt_length == -1) {
LOG("An error occurred in public_encrypt() method");
}
LOG("Data has been encrypted.");
create_encrypted_file(encrypt, public_key);
LOG("Encrypted file has been created.");
free(public_key);
free(encrypt);
LOG("RSA has been finished.");
return 0;
}
I've seen many posts and haven't found any fix, even though that this one was extremely similar to my issue
.
Load public key to create rsa object for public encryption
Both keys are public RSA keys with a size of 512 bits and an exponent of 17, specified in PKCS1-PEM format.
The message m, the modulus n_fail of the not working key and the modulus n_ok of the working key are:
m = 0xc8cd2174b98433b93094b36026de125a7f5ed85ec27ee6bb9e996cb3b938e9c6238cc65d3615fb635f6f080f6dda06315928bcae4ccf802f9680547db57b8283
n_fail = 0xc4f75716ec835d2325689f91ff85ed9bfc3211db9c164f41852e264e569d2802008054a0ef459e7e3eabb87fae576e735434d1d124b30b11bd6de09814860155
n_ok = 0xec83fe109b5e77fcfafc251aee0b44a756f4eb5a0fe520f85e7b322993055e5a3373f653035cd52374af2f4207cd50244961b9f16087abb807704f34bf6c35db
A comparison shows that
n_fail < m < n_ok
For RSA the condition m < n must apply. This condition is violated for n_fail, which is the cause of the issue. This also means that the corresponding key itself is not invalid. It can be used to encrypt messages that do not violate m < n. For the posted message, however, its modulus is too small.
Regarding security: Nowadays the key should have a size of 2048 bits and one of the paddings specified in RFC8017 (RSAES-PKCS1-v1_5 or RSAES-OAEP) should be used.
The error is actually mathematically related. Neither the key nor the the code is causing the issue. If you change the key or the data to encrypt, it'll work fine for some reason. So i guess something is happening during RSA calculation, but no smart enough to figure out what.

Verifying message signature produce HEADER TOO LONG error

Background
I am trying to verify signature of a given binary file using openssl. Actual signing of binary hash is done by a 3rd party. Both 3rd party and I have the same exact certificate - they sent me the certificate.
I have verified health of my certificate by running openssl x509 -noout -text -inform DER -in CERT_PATH. This displays contents of cert correctly.
Following is my code so far - I based it on openssl wiki example here:
static std::vector<char> ReadAllBytes(char const* filename){
std::ifstream ifs(filename, std::ios::binary|std::ios::ate);
std::ifstream::pos_type pos = ifs.tellg();
std::vector<char> result(pos);
ifs.seekg(0, std::ios::beg);
ifs.read(result.data(), pos);
return result;
}
int main(int ac, const char * av[]) {
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
// most of error check omitted for brevity
auto foundBinBytes = ReadAllBytes("BINARY_PATH");
auto foundSgnBytes = ReadAllBytes("SIGNATURE_PATH");
auto foundCertBytes = ReadAllBytes("CERT_PATH");
ERR_clear_error();
BIO *b = NULL;
X509 *c;
b = BIO_new_mem_buf(reinterpret_cast<const unsigned char *>(foundCertBytes.data()), foundCertBytes.size());
c = d2i_X509_bio(b, NULL);
EVP_MD_CTX* ctx = NULL;
ctx = EVP_MD_CTX_create();
const EVP_MD* md = EVP_get_digestbyname("SHA256");
int rc = EVP_DigestInit_ex(ctx, md, NULL);
EVP_PKEY *k = NULL;
k = X509_get_pubkey(c);
rc = EVP_DigestVerifyInit(ctx, NULL, md, NULL, k);
rc = EVP_DigestVerifyUpdate(ctx, reinterpret_cast<const unsigned char *>(foundBinBytes.data()), foundBinBytes.size());
ERR_clear_error();
rc = EVP_DigestVerifyFinal(ctx, reinterpret_cast<const unsigned char *>(foundSgnBytes.data()), foundSgnBytes.size());
ERR_print_errors_fp( stdout );
// openssl free functions omitted
if(ctx) {
EVP_MD_CTX_destroy(ctx);
ctx = NULL;
}
return 0;
}
Issue
Running this code produces following errors:
4511950444:error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long:/.../crypto/asn1/asn1_lib.c:152:
4511950444:error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header:/.../crypto/asn1/tasn_dec.c:1152:
4511950444:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:/.../crypto/asn1/tasn_dec.c:314:Type=X509_SIG
Question
What is wrong with my setup/code? Did I miss something along the way?
You never check the errors when reading the files. You might have errors there (does the file "CERT_PATH" exist? Do you have read permissions? ...).
If "CERT_PATH" cannot be read, then foundCertBytes.data() is an empty byte array, and this explains the subsequent errors.
If you get these errors in a mosquitto secured server's log, check the config file.
My mosquitto.conf was containing :
require_certificate true
my meross device doesn't send certificate. Turning this to "false" solved my problem after restart

error:0906D06C:PEM routines:PEM_read_bio:no start

Getting this very annoying error. error:0906D06C:PEM routines:PEM_read_bio:no start
Code:
RSA* publickey = cWrapper.getPublicKey("C:/rsa-stuff/public.pem");
QByteArray plain = "The man in the black fled into the desert and the gunslinger followed...";
QByteArray encrypted = cWrapper.encryptRSA(publickey, plain);
In encryptRSA():
const char* publicKeyStr = data.constData();
qDebug() << publicKeyStr;
BIO* bio = BIO_new_mem_buf((void*)publicKeyStr, -1);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
RSA* rsaPubKey = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL);
if(!rsaPubKey) {
qDebug() << "Could not load public key, " << ERR_error_string(ERR_get_error(), NULL); // error is here
}
BIO_free(bio);
This is how I read file:
QByteArray data;
QFile file(filename);
if(!file.open(QFile::ReadOnly))
{
printf("Error reading file: %s\n", file.errorString());
return data;
}
data = file.readAll();
file.close();
return data;
When I print out publicKeyStr, looks fine. This is notepad++ view with all characters enabled:
Anyone know what I am doing wrong? Super annoying issue :(
First of all, it's not this problem because I don't get the trusted part. Anyhow, I did try all the "solutions" and none of them worked, same error.
Your RSA public key is in SubjectPublicKeyInfo PEM format, but you are trying to read it using PEM_read_bio_RSAPublicKey which tries to read a PEM RSA key in PKCS#1 format. Try using PEM_read_bio_RSA_PUBKEY instead.
https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_RSAPublicKey.html
I got that same error on an openSSL1.1.0f I ported. The error showed up in my logger when reading out the root certificate from an mqtt client connection, until I figured out that I had forwarded the ERR_put_error() directly to my logger, whereas in openssl - the "real" error handling is kept in an ERR_STATE error buffer, and so sometimes (like in this case), errors are "expected", and the ERR_STATE error buffer is cleared (before anyone should check it).
in crypto/pem/pem_info.c, line 65:
i = PEM_read_bio(bp, &name, &header, &data, &len);
if (i == 0) {
error = ERR_GET_REASON(ERR_peek_last_error());
if (error == PEM_R_NO_START_LINE) {
ERR_clear_error();
break;
}
goto err;
meaning it runs througth the BIO_gets inside the PEM_read_bio until it returns zero, and if you get this PEM_R_NO_START_LINE, then thats just a way of saying its done.
By that time though, the error had already landed in my logger. So for anyone being confused by errors he or she is forwarding directly from ERR_put_error, use the ERR_print_errors_fp(stderr); in your errorhandling routine instead. In my case, as I dont have a stderr, I made a patched version of it, like:
void errorhandling()
{
unsigned long l;
char buf[256];
const char *file, *data;
int line, flags;
while ((l = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0)
{
ERR_error_string_n(l, buf, sizeof buf);
printf("%s:%s:%d:%s\n", buf, file, line, (flags & ERR_TXT_STRING) ? data : "");
}
}

OpenSSL AES_cfb128_encrypt public/private key C++

I have very basic encrypt/decrypt application that uses constant key. How to make this application to work with public/private key? Is it enough to generate keys with openssl and use them in my code variable ckey?
Can I somehow generate keys with my library?
#include "stdafx.h"
#include <openssl/aes.h>
#include <algorithm>
#include <iostream>
int _tmain(int argc, _TCHAR* argv[])
{
int bytes_read, bytes_written;
unsigned char indata[AES_BLOCK_SIZE + 1];
unsigned char outdata[AES_BLOCK_SIZE + 1];
std::fill(indata, indata + AES_BLOCK_SIZE, 0);
std::fill(outdata, outdata + AES_BLOCK_SIZE, 0);
/* ckey and ivec are the two 128-bits keys necesary to
en- and recrypt your data. Note that ckey can be
192 or 256 bits as well */
unsigned char ckey[] = "thiskeyisverybad";
const char ivecstr[AES_BLOCK_SIZE] = "goodbyworldkey\0";
unsigned char ivec[] = "dontusethisinput";
/* data structure that contains the key itself */
AES_KEY key;
/* set the encryption key */
AES_set_encrypt_key(ckey, 128, &key);
/* set where on the 128 bit encrypted block to begin encryption*/
int num = 0;
FILE* ifp;
FILE* oefp;
FILE* odfp;
ifp = fopen("infile.txt", "r");
if (ifp == NULL) perror("Error opening file");
oefp = fopen("outEncryptfile.txt", "w");
if (oefp == NULL) perror("Error opening file");
odfp = fopen("outDecryptfile.txt", "w");
if (odfp == NULL) perror("Error opening file");
int b = 0;
int w = 0;
memcpy(ivec, ivecstr, AES_BLOCK_SIZE);
while (1)
{
std::fill(indata, indata + AES_BLOCK_SIZE, 0);
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, ifp);
b = b + bytes_read;
indata[AES_BLOCK_SIZE] = 0;
//std::cout << "original data:\t" << indata << std::endl;
std::cout << indata;
AES_cfb128_encrypt(indata, outdata, bytes_read, &key, ivec, &num,AES_ENCRYPT);
bytes_written = fwrite(outdata, 1, bytes_read, oefp);
w = w + bytes_written;
if (bytes_read < AES_BLOCK_SIZE)
break;
}
fclose(oefp);
oefp = fopen("outEncryptfile.txt", "r");
if (oefp == NULL) perror("Error opening file");
b = 0;
memcpy(ivec, ivecstr, AES_BLOCK_SIZE);
while (1)
{
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, oefp);
b = b + bytes_read;
indata[AES_BLOCK_SIZE] = 0;
std::cout << "original data:\t" << indata << std::endl;
AES_cfb128_encrypt(indata, outdata, bytes_read, &key, ivec, &num, AES_DECRYPT);
std::cout << "decrypted data:\t" << outdata << std::endl;
bytes_written = fwrite(outdata, 1, bytes_read, odfp);
if (bytes_read < AES_BLOCK_SIZE)
break;
}
fclose(odfp);
return 0;
}
I have very basic encrypt/decrypt application that uses constant key. How to make this application to work with public/private key? Is it enough to generate keys with openssl and use them in my code variable ckey?
You can't. Shared key and private key cryptography are sufficiently different to ensure you cannot do it. Or maybe more correctly, you can't do it without a major redesign and rewrite.
OpenSSL provides two (maybe three) high level primitives of interest for your issue. Here's the documentation on them:
EVP Symmetric Encryption and Decryption
EVP Asymmetric Encryption and Decryption
The "maybe three" is:
EVP Authenticated Encryption and Decryption
Here are the common API calls when using (1) EVP Symmetric Encryption and (2) EVP Asymmetric Encryption from above:
EVP Symmetric Encryption
EVP_CIPHER_CTX_new
EVP_EncryptInit_ex
EVP_EncryptUpdate
EVP_EncryptFinal_ex
EVP Asymmetric Encryption
EVP_CIPHER_CTX_new
EVP_SealInit
EVP_SealUpdate
EVP_SealFinal
EVP_CIPHER_CTX_free
With all that said, its not really a design problem in OpenSSL. The APIs are well defined for operation at hand - symmetric encryption, authenticated encryption, asymmetric encryption, signing, verifying, hashing, mac'ing, etc. It would be difficult to shoehorn everything into one set of API calls.
The code you provided, using AES_KEY, AES_set_encrypt_key, and friends, is even harder to work with. Its a specialized software-only AES implementation that you get if you configure with no-asm. It also has some landmines, like being non-portable in some cases. For example, I seem to recall special care has to be taken on some big-endian platforms because you need to byte-swap the key.
Yu might also want to look at how IPsec and TLS do things. They break workflows up into two parts: (1) key exchange (or key transport) and (2) bulk encryption. The key exchange portion happens using public/private key cryptography. and establishes a shared secret. Once the shared secret is established, symmetric ciphers are keyed and bulk encryption takes place.
IPsec has a more defensive security posture. TLS runs a bit fast and loose for my taste. I only use TLS when requirements tell me to do so (like interop'ing with existing systems). Otherwise, I prefer in-app VPNs or IPsec-like schemes.