Let say I want to have a code doing something like this openssl command in my c++ application.
openssl rsa -in private.pem -pubout -outform der -out ./out.pub
How can I do that?
I was look for a sample on github and came up with follow scheme.
key = PEM_read_bio_RSAPrivateKey(bio, NULL, 0, NULL);
len = i2d_RSAPublicKey(key, &bufp);
It returns different value than I'm getting from command line tool. I suppose there was no conversion from private key to public and it just saves the private key. Could any one tell me the right approach for getting pub key from private using openssl lib. I would also greatly appreciate any links to pub\priv key examples for openssl.
Finally I've just find proper sources inside openssl itself. Here is exactly what happens during
openssl rsa -in private.pem -pubout -outform der -out ./out.pub
I've dropped lots of checks from original code to make it smaller.
#include <openssl/pem.h>
#include <openssl/x509.h>
EVP_PKEY *load_key(const char *file)
{
BIO *key=NULL;
EVP_PKEY *pkey=NULL;
key=BIO_new(BIO_s_file());
BIO_read_filename(key,file);
pkey=PEM_read_bio_PrivateKey(key,NULL,NULL,NULL);
return pkey;
}
int _tmain(int argc, _TCHAR* argv[])
{
BIO *out=NULL;
out=BIO_new(BIO_s_file());
EVP_PKEY *pkey;
RSA *rsa=NULL;
char *infile = path_to_pem;
char *outfile = path_to_der;
pkey = load_key(infile);
if (pkey != NULL)
rsa = EVP_PKEY_get1_RSA(pkey);
EVP_PKEY_free(pkey);
BIO_write_filename(out,outfile);
i2d_RSA_PUBKEY_bio(out,rsa);
}
Related
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
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.
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_);
How do I get the Microsoft's CryptoAPI CryptImportKey function to import a PEM encoded key? It actually works but CryptDecrypt returns an error.
// 1. Generate a Public/Private RSA key pair like so:
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -out public.pem -outform PEM -pubout
// 2. Create a digital signaure using OpenSSL
// Load Private key
// -----BEGIN RSA PRIVATE KEY-----
// BLAHBLAHBLAH
// -----END RSA PRIVATE KEY-----
// Concat user details
std::string sUser = "John Doe | Business | john#na.com | 1316790394 | 0 | 1 | ProductName | 1";
// Get a one-way hash of it.
SHA1((const unsigned char *) sUser.c_str(),sUser.size(), hash);
// Create the digital signature ~ PKCS #1 v2.0 format (also known as OAEP encryption)
RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, pbData, &iDataLen, rsa_key);
// 3. Verify the signature using Windows CryptoAPI
// Load Public key
// -----BEGIN PUBLIC KEY-----
// BLAHBLAHBLAH
// -----END PUBLIC KEY-----
// Convert from PEM format to DER format - removes header and footer and decodes from base64
CryptStringToBinaryA((char*)pbPublicPEM, iPEMSize, CRYPT_STRING_ANY, pbPublicDER, &iDERSize, NULL, NULL);
// Decode from DER format to CERT_PUBLIC_KEY_INFO. This has the public key in ASN.1 encoded
// format called "SubjectPublicKeyInfo" ... szOID_RSA_RSA
// Do I need to get the "public key" and "modulus" from this format and build a PUBLICKEYBLOB manually?
CryptDecodeObjectEx( X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pbPublicDER, iDERSize, CRYPT_ENCODE_ALLOC_FLAG, NULL, &pbPublicPBLOB, &iPBLOBSize );
// decode the RSA Public key itself to a PUBLICKEYBLOB ?
CryptDecodeObjectEx( X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, pbPublicPBLOB->PublicKey.pbData, pbPublicPBLOB->PublicKey.cbData, CRYPT_ENCODE_ALLOC_FLAG, NULL, &pbPKEY, &iPKEYSize );
// Get a context
CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
// load the key
CryptImportKey(hCryptProv, pbPKEY, iPKEYSize, 0, CRYPT_OAEP, &hKey);
// Verify the signature
CryptDecrypt(hKey, 0, TRUE, 0, pbData, &iDataLen);
// CryptDecrypt returns NTE_NO_KEY -2146893811 0x8009000D
You are using the wrong APIs. RSA_sign() signs a hash; use CryptVerifySignature() to verify it.
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.
}