RSA public encryption using public key from X509 certificate c++ - c++

The task is to encrypt some message with RSA public key which lies in .crt file - X509 certificate file.
With issue below I succesfully get sertificate information, but can not find any issue how to use public key from x to encrypt message using RSA algorythm.
When I trying to create BIO buffer with key data and to use PEM_read_bio_RSA_PUBKEY function to write key into RSA struct it returns an error "error:0906D06C:PEM routines:PEM_read_bio:no start line" which is clear why appear but not clear how to correctly solve. Are any issue how I can do encryption without creating another buffer and adding manualy lines with "-----BEGIN RSA PUBLIC KEY-----" and "-----END RSA PUBLIC KEY-----"?
Here are my code for this job:
unsigned char message[] = "Hello cryptographic world!";
unsigned char *encrypted_text;
int CyferWithHostKeyPub()
{
ERR_load_crypto_strings();
int res_length = 0;
RSA *public_key;
X509 *x;
BIO *fp = BIO_new_file("cert.crt", "r");
if (!fp)
{
printf("\nCan't open file.\n");
return -1;
}
x = PEM_read_bio_X509(fp, 0, 0, NULL);
if (!x) {
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
return 1;
}
BIO *membuf = BIO_new_mem_buf((void*)x->cert_info->key->public_key->data, x->cert_info->key->public_key->length);
public_key = PEM_read_bio_RSA_PUBKEY(membuf, &public_key, NULL, NULL);
if (!public_key) {
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
return 1;
}
int encrypted_length = RSA_size(public_key);
encrypted_text = new unsigned char[ encrypted_length ];
RSA_public_encrypt(sizeof(message), message, encrypted_text, public_key, RSA_PKCS1_PADDING);
RSA_free(public_key);
X509_free(x);
BIO_free(membuf);
BIO_free(fp);
return encrypted_length;
}
So, how can I do this correctly? Thanks!

Use the X509_get_pubkey() function to obtain a reference to the EVP_PKEY structure contained within the X509 structure. Then you can use EVP_PKEY_get1_RSA() to get a reference to the RSA structure.
See these man pages for further info:
https://www.openssl.org/docs/man1.1.0/crypto/X509_get_pubkey.html
https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_RSA.html

Related

usage of RSA_NO_PADDING when decrypting data with RSA private key using openssl libcrypto

I generated RSA private key and public key as below,
openssl genpkey -algorithm RSA -out key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in pri.key -out pub.key
And encrypted text file as below,
openssl pkeyutl -encrypt -pubin -inkey ~/pub.key -in ~/1.txt -out ~/1e.txt
Then I wrote below program to decrypt the encryted file. However, it seemed that decrypt didn't work as expected.
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/conf.h>
#include <iostream>
using namespace std;
void
cleanup()
{
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
}
int
main(int argc, char** argv)
{
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
OPENSSL_config(nullptr);
cout<<"Initialize crypto library done"<<endl;
EVP_PKEY * key = EVP_PKEY_new();
if (key == nullptr) {
cout<<"Failed to contruct new key"<<endl;
return 1;
}
FILE * fpri = nullptr;
fpri = fopen("/home/stack/pri.key", "r");
if (fpri == nullptr) {
cout<<"Failed to load private key"<<endl;
return 1;
}
key = PEM_read_PrivateKey(fpri, &key, nullptr, nullptr);
if (key == nullptr) {
std::cout<<"Read private key failed"<<endl;
return 1;
}
cout<<"load private key successfully"<<endl;
EVP_PKEY_CTX *ctx = nullptr;
ctx = EVP_PKEY_CTX_new(key, nullptr);
EVP_PKEY_decrypt_init(ctx);
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING);
size_t outlen = 0, inlen = 0;
unsigned char * out = nullptr, * in = nullptr;
char buf[1024];
FILE * fe = nullptr;
fe = fopen("/home/stack/1e.txt", "r");
size_t len = fread(buf, 1, sizeof(buf), fe);
cout<<"data input length is "<<len<<endl;
EVP_PKEY_decrypt(ctx, NULL, &outlen, in, inlen);
cout<<"outlen is "<<outlen<<endl;
out = (unsigned char*)OPENSSL_malloc(outlen);
EVP_PKEY_decrypt(ctx, out, &outlen, in, inlen);
cout<<"decrypted data "<<out<<endl;
cleanup();
return 0;
}
When executing the code, the result is as below,
[stack#agent ~]$ ./test
Initialize crypto library done
load private key successfully
data input length is 256
outlen is 256
decrypted data
It seemed the decrypted data length was not correct and not printable.
When I commented out the instruction "EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING);", it worked well.
I also tried with RSA_PKCS1_OAEP_PADDING, it didn't work either. If RSA PADDING is not set, it worked.
My question is as below,
Which padding is used in following command?
openssl pkeyutl -encrypt -pubin -inkey ~/pub.key -in ~/1.txt -out ~/1e.txt
Is padding necessary for RSA encrypt/decrypt? If so, how could I apply the padding mechanism?
You should use EVP_PKEY_CTX_set_rsa_padding if you use not default padding in openssl pkeyutl encryption. See openssl pkeyutl documentation for details about -rsa_padding_mode.

PKCS#12 Invalid Password

I am using this OpenSSL code on the iPhone to generate a PKCS#12 file given some data of a certificate/private key. I am able to verify that this PKCS#12 is parseable on OpenSSL, since it doesn't print out an error when I check for it in the code. However, when I transfer it to my server, it says: Error: PKCS#12 MAC could not be verified. Invalid password? Does anyone know why this is? I am using the same password, which is 'password'
- (NSData *)generateP12AsNSData:(NSData *)keyData certificate:(NSData *)certificateData {
//put the certificateData into a X509 certificate
const unsigned char *certificateDataBytes = (const unsigned char*)[certificateData bytes];
X509 *certificateX509 = d2i_X509(NULL, &certificateDataBytes, [certificateData length]);
EVP_PKEY *privateKey;
PKCS12 *p12;
//cast the key data as an unsigned char so that we can convert it to the OpenSSL key format
const unsigned char *bits = (unsigned char *) [keyData bytes];
int length = (int)[keyData length];
privateKey = EVP_PKEY_new();
//convert the unsigned char to OpenSSL Key format
RSA *rsa = NULL;
d2i_RSAPrivateKey(&rsa, &bits, &length);
EVP_PKEY_assign_RSA(privateKey, rsa);
//create the PKCS#12
OpenSSL_add_all_algorithms();
p12 = PKCS12_create("password", "ExtraDeviceP12", privateKey, certificateX509, NULL, 0,0,0,0,0);
//make sure the p12 exists
if(!p12) {
fprintf(stderr, "Error creating PKCS#12 ");
ERR_print_errors_fp(stderr);
return nil;
}
//error checking to make sure we generated the CSR correctly
STACK_OF(X509) *ca = NULL;
EVP_PKEY *parseKey;
X509 *parseCert;
if (!PKCS12_parse(p12, "password", &parseKey, &parseCert, &ca)) {
printf("error parsing PKCS#12 file");
return nil;
}
//convert the PKCS#12 to binary data
//create a new memory BIO. A BIO is used for basic I/O abstraction.
BIO *bio;
bio = BIO_new(BIO_s_mem());
//i2d_PKCS12_bio is used to export a PKCS12 object
i2d_PKCS12_bio(bio, p12);
BUF_MEM *buffer;
BIO_get_mem_ptr(bio, &buffer);
//int bioLen = BIO_pending(&buffer);
char *buff = (char*)malloc(buffer->length);
memcpy(buff, buffer->data, buffer->length - 1);
buff[buffer->length - 1] = 0;
NSData *data = [NSData dataWithBytes:buff length:buffer->length];
NSString *string = [data base64EncodedStringWithOptions:0];
NSLog(#"Base64 PKCS#12: %#", string);
BIO_free_all(bio);
return data;
}
EDIT:
Here is the code on my server side written in Javascript. In this case req.body is the NSData sent from the iPhone. I get the invalid password error.
var p12b64 = req.body.toString('base64');
var p12Der = forge.util.decode64(pk12b64);
var p12Asn1 = forge.asn1.fromDer(p12Der);
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, 'password');
Try the following. It shows you where some key return values should be checked, and it avoids extra copies of the data:
BIO *bio = BIO_new(BIO_s_mem());
ASSERT(bio != NULL);
int ret = i2d_PKCS12_bio(bio, p12);
ASSERT(ret == 1);
BUF_MEM *buffer;
BIO_get_mem_ptr(bio, &buffer);
ASSERT(buffer != NULL);
NSData *data = [NSData dataWithBytes:buffer->data length:buffer->length];
BIO_free_all(bio);
You can find the docs for i2d_PKCS12_bio at i2d_PKCS12_bio man pages.
If you want, you can Base64 encode the binary data from i2d_PKCS12_bio. See Non-printable character after generating random n-byte Base64 string for using a BIO chain and ensuring the Base64 string is NULL terminated.

How to use openssl lib pem_read to read public/private key from a string

I use openssl in C++ , copy the PrivateKey and PublicKey file content to two const char*,
but I see the demo of api is only get pem public/private key from a pem file,so how can I get a public/private key from a string?
Try this:
char* mKey="-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCDT4ntP3Fqj73RQW32B6hCDHMG64GtxeQDZ5BcQnQSRB3S/EwM\ngpZwuGYwTb7E65pdAAQ0e5na2d7yIGZX4MoDRGaDbYgdxr49J430cVLRU1r9EW+O\nqZQERyGYefxWOqBaNZL2PBclS/qf+AxRh1WnD8aY5V5zNItgVV4Bv9w4YQIDAQAB\nAoGAMd6xaXNg5bG1y5Vn57q8wmjr/sLOu2qe0FQy1Xc/kfhgw1Kip1WpQSInXU0J\nmMxKEewBrNUMd7urGpYHiHSNA+QXiIp3qxGy7PrmZrPSrJImPxAE02YaUGDoh+6o\nZJc7xXCw2bwX8Fth8Duj2KNcIDuWuieybfzwTYKKJG3J04ECQQDxSa4gq/0SiiZ2\nc8YTn9wCTwqezmdI8SWsWXRnpXt1BhejokvLFbqpfQZ6m9LLYvpUsihZ2QkBXUl/\n1/uNu+aJAkEAi1Ey/7fjJJSJalNUgF3lKQdTqUlYI/9O9/98zPOcDmXcKlLNpf+f\nTV3nhK3vsewYqsx3Tu9bMKBVTE0dv+/NGQJAHfYyQyhYMpcpE4hozkMJhNffz7x9\notcfAHnTNJOd8vggs1cR5lP6a9V0moEC+fJ+d0nwLMgAkETPParKN91fUQJAEWMB\n3V4ir+cFu0pJCngtaFBsxXzMzjlHrrWo6p8gg798mZ+Z4LSlOe+VPD7E4kyXy4EX\nBrfihpAL9SjOpKyVyQJBAPD3E4Z7THZCQI/2u4eRXz3qbJAmPYLPTn/AxuX4VssW\n1WJAxZeCFHWL6+/84zoDWwzXN0xQFzO0ZspxxQNFqCI=\n-----END RSA PRIVATE KEY-----";
BIO* bo = BIO_new( BIO_s_mem() );
BIO_write( bo, mKey,strlen(mKey));
EVP_PKEY* pkey = 0;
PEM_read_bio_PrivateKey( bo, &pkey, 0, 0 );
BIO_free(bo);
RSA* rsa = EVP_PKEY_get1_RSA( pkey );
Here is the code to get public key from string in C++ using the openssl library.
#include <openssl/bio.h>
#include <openssl/rsa.h>
const std::string RsaPublicKey = "-----BEGIN PUBLIC KEY---";
RSA* public_key_;
BIO* bo = BIO_new(BIO_s_mem());
BIO_write(bo, RsaPublicKey.c_str(), RsaPublicKey.length());
PEM_read_bio_RSA_PUBKEY(bo, &public_key_, nullptr, nullptr);
BIO_free(bo);
RSA_free(public_key_);

Save RSA public and private keys in XML (w3c) format via OpenSSL (c++)

I want to work with XML file format of RSA public and private keys.
Now i found how to save these keys in PEM and binary (DER) formats (for example, PEM_write_RSAPrivateKey())
I have a string with xml-format RSA keys and i need to load them into EVP_PKEY or RSA OpenSSL structures.
XML format is like this:
<RSAKeyPair>
<Modulus>...</Modulus>
<Exponent>...</Exponent>
<P>...</P>
<Q>...</Q>
<DP>...</DP>
<DQ>...</DQ>
<InverseQ>
...
</InverseQ>
<D>...</D>
</RSAKeyPair>
Thanks!
//just a code for demo,not for actually use
int len;
RSA *rsa;
BIO *bio;
unsigned char *data;
bio = BIO_new(BIO_s_meme());
BIO *b64;
b64 = BIO_new(BIO_f_base64());
BIO_write(bio, "<RSAKeyPair>\n",strlen("<RSAKeyPair>\n"));
//write Modulus
len=BN_num_bytes(rsa->n);
data=(unsigned char *)OPENSSL_malloc(len);
if(data) {
BIO_write(bio," <Modulus>",strlen(" <Modulus>"));
BN_bn2bin(rsa->n,data);
bio = BIO_push(b64, bio);
BIO_write(bio, data, len);
(void)BIO_flush(bio);
BIO_pop(bio);
BIO_reset(b64);
BIO_write(bio,"</Modulus>",strlen("</Modulus>"));
}
//write Exp
...
//write the bignum in rsa structure you want
BIO_write(bio, "</RSAKeyPair>\n",strlen("</RSAKeyPair>"));

how to convert the Certificate String into X509 structure.?

can any one tell me how to convert the string content into X509 structure . i am using openssl to read the X509 Structure.
example : certificate string
-----BEGIN CERTIFICATE-----
MIIExDCCA6ygAwIBAgIJAK0JmDc/YXWsMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD
VQQGEwJJTjELMAkGA1UECBMCQVAxDDAKBgNVBAcTA0hZRDEZMBcGA1UEChMQUm9j
a3dlbGwgY29sbGluczEcMBoGA1UECxMTSW5kaWEgRGVzaWduIENlbnRlcjEOMAwG
A1UEAxMFSU1BQ1MxKTAnBgkqhkiG9w0BCQEWGmJyYWphbkBSb2Nrd2VsbGNvbGxp
bnMuY29tMB4XDTExMDYxNjE0MTQyM1oXDTEyMDYxNTE0MTQyM1owgZwxCzAJBgNV
BAYTAklOMQswCQYDVQQIEwJBUDEMMAoGA1UEBxMDSFlEMRkwFwYDVQQKExBSb2Nr
d2VsbCBjb2xsaW5zMRwwGgYDVQQLExNJbmRpYSBEZXNpZ24gQ2VudGVyMQ4wDAYD
VQQDEwVJTUFDUzEpMCcGCSqGSIb3DQEJARYaYnJhamFuQFJvY2t3ZWxsY29sbGlu
cy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDfjHgUAsbXQFkF
hqv8OTHSzuj+8SKGh49wth3UcH9Nk/YOug7ZvI+tnOcrCZdeG2Ot8Y19Wusf59Y7
q61jSbDWt+7u7P0ylWWcQfCE9IHSiJIaKAklMu2qGB8bFSPqDyVJuWSwcSXEb9C2
xJsabfgJr6mpfWjCOKd58wFprf0RF58pWHyBqBOiZ2U20PKhq8gPJo/pEpcnXTY0
x8bw8LZ3SrrIQZ5WntFKdB7McFKG9yFfEhUamTKOffQ2Y+SDEGVDj3eshF6+Fxgj
8plyg3tZPRLSHh5DR42HTc/35LA52BvjRMWYzrs4nf67gf652pgHh0tFMNMTMgZD
rpTkyts9AgMBAAGjggEFMIIBATAdBgNVHQ4EFgQUG0cLBjouoJPM8dQzKUQCZYNY
y8AwgdEGA1UdIwSByTCBxoAUG0cLBjouoJPM8dQzKUQCZYNYy8ChgaKkgZ8wgZwx
CzAJBgNVBAYTAklOMQswCQYDVQQIEwJBUDEMMAoGA1UEBxMDSFlEMRkwFwYDVQQK
ExBSb2Nrd2VsbCBjb2xsaW5zMRwwGgYDVQQLExNJbmRpYSBEZXNpZ24gQ2VudGVy
MQ4wDAYDVQQDEwVJTUFDUzEpMCcGCSqGSIb3DQEJARYaYnJhamFuQFJvY2t3ZWxs
Y29sbGlucy5jb22CCQCtCZg3P2F1rDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB
BQUAA4IBAQCyYZxEzn7203no9TdhtKDWOFRwzYvY2kZppQ/EpzF+pzh8LdBOebr+
DLRXNh2NIFaEVV0brpQTI4eh6b5j7QyF2UmA6+44zmku9LzS9DQVKGLhIleB436K
ARoWRqxlEK7TF3TauQfaalGH88ZWoDjqqEP/5oWeQ6pr/RChkCHkBSgq6FfGGSLd
ktgFcF0S9U7Ybii/MD+tWMImK8EE3GGgs876yqX/DDhyfW8DfnNZyl35VF/80j/s
0Lj3F7Po1zsaRbQlhOK5rzRVQA2qnsa4IcQBuYqBWiB6XojPgu9PpRSL7ure7sj6
gRQT0OIU5vXzsmhjqKoZ+dBlh1FpSOX2
-----END CERTIFICATE-----
this the certificate i am going to get as string input.how to convert this into X509.
You can use this OpenSSL code snippet to load the certificate when provided as a string input:
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
const unsigned char *data =
"-----BEGIN CERTIFICATE-----\n"
"MIIExDCCA6ygAwIBAgIJAK0JmDc/YXWsMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD\n"
/*...*/
"gRQT0OIU5vXzsmhjqKoZ+dBlh1FpSOX2\n"
"-----END CERTIFICATE-----";
BIO *bio;
X509 *certificate;
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, data);
certificate = PEM_read_bio_X509(bio, NULL, NULL, NULL);
Hope this helps.
Below code is not complete code to run. It gives what the API and how to use them:
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
const unsigned char data[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIExDCCA6ygAwIBAgIJAK0JmDc/YXWsMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD\n"
/*...*/
"gRQT0OIU5vXzsmhjqKoZ+dBlh1FpSOX2\n"
"-----END CERTIFICATE-----";
BIO *bio;
X509 *certificate;
bio = BIO_new(BIO_s_mem());
// create BIO structure which deals with memory
lTemp= BIO_write(lBio, (const void*)data, sizeof(data));
// Note Bio write and BIO puts do the exact thing difference is u need to give
// the size of input data to copy in the Bio write. (Safe and best use bio write)
// Check lTemp should be equal to the size of data or number of characters
// in the data u want to copy....
// these values are defined in my code not in the library
if (iFileType == DERFORMAT)
{
certificate = d2i_X509_bio(bio, NULL);
// this line will decode the DER format certificate
// and convert that into X509 formatted certificate.
}
else if (iFileType == PEMFORMAT)
{
certificate = PEM_read_bio_X509(bio, NULL, 0, NULL);
// this line will decode the PEM certificate
// and convert that into X509 formatted certificate.
}