Writing PEM RSA private key in C - c

Hello i want my function to write a pem file from my RSA.
void write_privatekey(RSA *rsa, BIO *keybio)
{
EVP_PKEY *pkey;
BIO *bio_private;
pkey = PEM_read_bio_PrivateKey(keybio, &pkey, 0, 0);
bio_private = BIO_new_file("private_new.pem", "w+");
PEM_write_bio_PrivateKey(bio_private, pkey, NULL, NULL, 0, NULL, NULL);
}
but when i try to run this code it segfault
[1] 48767 segmentation fault ./corsair public.key

The OpenSSL APIs are not the most intuitive to use. However it should be a huge warning sign for you, that you passed a pointer to pkey to PEM_read_bio_PrivateKey and also assign its return value to it.
If you look at the reference manual the suggested stanza is
key = PEM_read_bio_PrivateKey(bp, NULL, pass_cb, …);
if( key == NULL ){
/* Error */
}
Your code snipped lacks a couple of things: It doesn't provide a pass phrase callback and it doesn't perform error checking. You absolutely must do both things.

After messing with the OpenSSL lib. and learned a way to dive into it documentation.
here it is the answer to write the PEM file from RSA object.
BIO *bio_private;
bio_private = BIO_new_file("private_new.pem", "w+");
PEM_write_bio_RSAPrivateKey(bio_private, rsa, 0, 0, 0, 0, 0);

Related

getting a segmentation fault with RSA_public_encrypt

Here's my code:
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <string.h>
int main (void)
{
char publicKey[] = "-----BEGIN RSA PUBLIC KEY-----\n"\
"MIIBCgKCAQEAqBa0jeqfHO8CFZuHyN5WgdTd3uThkU/I9lR+bb2R9khVU1tYhiyL\n"\
"Gfnm051K0039gCBAz9S7HHO2lqR/B1kiEGwBzg83N3tL2UzQXXbpxJz0b7vV8rXZ\n"\
"fB9j+FCAtD5znittXRjWrNqlEyJyxK+PuxNF3uPGWSjkYtRKv2f5HCXsfRJah40w\n"\
"5zU+OKZsZMrSSxweYN9Jwc++oMaSPCsGKog6NPbkbcTK8Akveof8v5rpG50I/zcQ\n"\
"9dCD69qJcmhm4ai4fAZFJlsFH2HmxV6DERFs3TNyzGlSQ1gd/XLER7U9nlrOUT87\n"\
"mXreUIXSkAFdCrlHXHOzj0eodfN8IfHjnQIDAQAB\n"\
"-----END RSA PUBLIC KEY-----\n";
char plaintext[] = "enp6";
// base64 decode plaintext
EVP_DecodeBlock(plaintext, plaintext, strlen(plaintext));
BIO* bo = BIO_new(BIO_s_mem());
BIO_write(bo, publicKey, strlen(publicKey));
EVP_PKEY* pkey = 0;
PEM_read_bio_PUBKEY(bo, NULL, NULL, NULL);
BIO_free(bo);
RSA* rsa = EVP_PKEY_get1_RSA(pkey);
char ciphertext[RSA_size(rsa)];
RSA_public_encrypt(strlen(plaintext), plaintext, ciphertext, rsa, RSA_NO_PADDING);
printf("%d\n", (int) strlen(ciphertext));
}
I'm trying to run it by doing gcc -x c test2.c -lcrypto && ./a.out. Any ideas as to what I'm doing wrong?
Your code contains several mistakes. The direct cause of the segfault is the fact that the variable pkey is never assigned any value after initializing it with 0. EVP_PKEY_get1_RSA(pkey) segfaults as a consequence. You probably intended to do
EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bo, NULL, NULL, NULL);
However, with the PEM that you posted, that will not work. The header of the PEM indicates that this is an RSA key stored in "traditional" format. PEM_read_bio_RSAPublicKey is capable of reading such keys:
RSA *rsa = PEM_read_bio_RSAPublicKey(bo, NULL, NULL, NULL);
Then your choice of RSA_NO_PADDING when invoking RSA_public_encrypt is not right. In order to use that, the plaintext must be the exact same length as the key size. You could try using RSA_PKCS1_PADDING instead.
Finally, like mentioned in the comment section as well, using strlen on cipher text is incorrect, because it is not a 0-terminated string.
In general, you should check all return values for all OpenSSL functions invoked, to see when/if something went wrong.

OpenSSL C API - getting keylength of public key in DER encoding

I would like to determine the key length of a public key encoded in DER format while using the OpenSSL API in C.
It is quite straight-forward to do this with a key encoded in PEM format:
FILE *fp = fopen(argv[1], "rb");
RSA *rsa_key = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
int keylength = RSA_size(rsa_key);
(assuming that the key to be loaded has been passed as an argument)
Unfortunately there is no equivalent DER_read_RSA_PUBKEY function.
I have been trying it with i2d_RSA_PUBKEY_fp and d2i_RSA_PUBKEY_fp but to no avail - only segmentation faults.
The difference between _RSA_PUBKEY and _RSAPublicKey seems to be that the former uses a SubjectPublicKeyInfo (certificate public key) structure and the latter uses a PKCS#1 RSAPublicKey structure.
The d2i_* function family is available for converting DER to openssl internal datastructures.
So, you should be able to use:
FILE *fp = fopen(argv[1], "rb");
RSA *rsa_key = d2i_RSAPublicKey_fp(fp, NULL);
int keylength = RSA_size(rsa_key);
to achieve what you want.

Convert X509_STORE_load_locations() call to load data from Memory

How can I convert the code below to load the file "cabundle.pem" from memory? The code is used in certificate verification (Source)
const char ca_bundlestr[] = "./cabundle.pem";
ret = X509_STORE_load_locations(store, ca_bundlestr, NULL);`
So what I currently did is put the cabundle.pem in string array and then add the cert to the store but it doesnt work since what I think I need to do is add it to the store using X509_STORE_CTX_trusted_stack but I do not know how to use STACK_OF(X509) I tried searching but I cant find anything.
const char ca_bundle[] = "-----BEGIN CERTIFICATE-----\n"
"MIIImDCCCD2gAwIBAgIRAMch/W0685E2zrArUB/79kUwCgYIKoZIzj0EAwIwgZIx\n"
"MIIImDCCC...............................................AwIwgZIx\n"
"CzAJBgNVB..........................................0ZXIxEDAOBgNV\n"
"-----END CERTIFICATE-----\n";
BIO *bio;
X509 *cert;
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, ca_bundle);
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
X509_STORE_add_cert(store, cert);
So how can I load my string array ca_bundle into a STACK_OF(X509) variable? Or if there is another way to do this please let me know. Thanks
I asked too early. I just found the answer to my question. Since the cabundle.pem contains more than 1 certificate you need to do it in a loop and read the next certificate then add it again to the store. You do not need the X509_STORE_CTX_trusted_stack since adding all the certificates to the store acts the same way as calling X509_STORE_load_locations
while (cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) {
X509_STORE_add_cert(store, cert);
}

Extracting the public certificate from a certificate

I would like to programmatically generate a certificate that contain only the public key using openssl.
So I basically need to do the following but using code.
openssl x509 -in certificate.crt -pubkey
Digging a bit it seem that I need to create a new certificate and copy all the information except the private key in it.
X509 * pNewKey = X509_new();
... Copy all the needed information ...
PEM_write_bio_X509(bioKey, &pNewKey, NULL, NULL);
Does someone have a better method?
I would like to programmatically generate a certificate that contain only the public key
To set the public key, take your public key in EVP_PKEY format and call X509_set_pubkey. Building upon your example code:
X509* x509 = X509_new();
EVP_PKEY* pkey = /***/;
int rc = X509_set_pubkey(x509, pkey);
ASSERT(rc == 1);
BIO* bio = BIO_new_file("filename.txt", "w");
ASSERT(bio != NULL);
rc = PEM_write_bio_X509(bio, x509, NULL, NULL);
ASSERT(rc == 1);
...
BIO_free(bio);
EVP_PKEY_free(pkey);
X509_free(x509);
For completeness, OpenSSL will save a certificate with no attributes, no version, no public key, and no signature.
So I basically need to do the following but using code.
openssl x509 -in certificate.crt -pubkey
OK, this is a different question. The above command will print the public key.
Use x509_get_pubkey:
EVP_PKEY* pkey = X509_get_pubkey(X509* x509);
Once you have the EVP_PKEY, you can get the type. You need to supply this function because OpenSSL does not provide it:
int EVP_PKEY_get_type(EVP_PKEY *pkey)
{
AC_ASSERT(pkey);
if (!pkey)
return NID_undef;
return EVP_PKEY_type(pkey->type);
}
Then, depending on the type, you can print the key:
if(type == EVP_PKEY_RSA || type == EVP_PKEY_RSA2)
{
RSA* rsa = EVP_PKEY_get1_RSA(pkey);
RSA_print_fp(stdout, rsa, 0);
RSA_free(rsa);
}
...
You have to break out the keys by type and then call the respective XXX_print_fp because OpenSSL does not provide an EVP_PKEY_print_fp.
The get1 in EVP_PKEY_get1_RSA means the reference count on the key is bumped. So you have to call a *_free on it. A get0 does not bump the reference count, and does not need a call to *_free.

How to encrypt data using RSA, with SHA-256 as hash function and MGF1 as mask generating function?

I was doing some experiments with cryptography. Now I have the public key of receiver and i want to encrypt some data and pass to the receiver.
I want to use RSAES-OAEP algorithm. with SHA-256 as hash function and MGF1 as mask generation function.
I want do this using openssl. I found a function RSA_public_encrypt() with this function we can specify the padding. One of the padding option available was
RSA_PKCS1_OAEP_PADDING
EME-OAEP as defined in PKCS #1 v2.0 with SHA-1 , MGF1 .
they are using sha-1.
I want to reconfigure the function to use SHA256 as hash function ans MGF1 as hash function. How can I do it ?
The following excerpt allows using OAEP with SHA256 for both the MGF and hash function. Tested with OpenSSL 1.0.2L
int flags = CMS_BINARY | CMS_PARTIAL | CMS_KEY_PARAM;
cms = CMS_encrypt(NULL, in, cipher, flags)
ri = CMS_add1_recipient_cert(cms, cert, flags);
pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING);
EVP_PKEY_CTX_set_rsa_oaep_md(pctx, EVP_sha256());
EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, EVP_sha256());
With a newer OpenSSL 1.0.2+ you can do it using the command:
openssl pkeyutl -in PlaintextKeyMaterial.bin -out EncryptedKeyMaterial.bin -inkey PublicKey.bin -keyform DER -pubin -encrypt -pkeyopt rsa_padding_mode:oaep
-pkeyopt rsa_oaep_md:sha256
This is taken from AWS KMS doc here: https://aws.amazon.com/es/premiumsupport/knowledge-center/invalidciphertext-kms/
OpenSSL uses definitions from PKCS #1 v2.0 and so the default for EME-OAEP is SHA-1 and MGF1. If you need to use SHA-256, you'll need to do the encoding yourself. This isn't terribly difficult however, see PKCS #1 v2.2 PDF Page 18 for details.
In the latest version of Openssl(1.0.2k) the signature of the API is changed which gives us more flexibility. Please find the details below,
int RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen,
const unsigned char *from, int flen,
int num, const unsigned char *param,
int plen, const EVP_MD *md,
const EVP_MD *mgf1md)
You can pass the EVP_MD structure to invoke the SHA-256 hashing using this.
The PKCS#1 doc referred to above is more mathematical and doesn't give coding examples, and the CMS answer is for ASN.1/SMIME data and not really relevant for the question asked, which was how to replace RSA_public_encrypt() which deals with simple binary/text data. I spent a whole day with trial and error and online searching to find the answer to this, and eventually got the answer (which was to use OpenSSL's EVP API) from looking at the source code of "openssl pkeyutl" - once I discovered that it was not difficult.
In my case I was looking to replace RSA_private_decrypt() for decryption using the private key and this is how to do that - it should be pretty easy to put to together an RSA_public_encrypt() replacement based on this:
EVP_PKEY *privKey = NULL;
BIO *bioPrivKey;
int outLen = 0, ret;
if ((bioPrivKey = BIO_new(BIO_s_mem())))
{
// Read the private key from the RSA context into the memory BIO,
// then convert it to an EVP_PKEY:
if ((ret = PEM_write_bio_RSAPrivateKey(bioPrivKey, rsaCtxt, NULL, NULL, 0, NULL, NULL)) &&
(privKey = PEM_read_bio_PrivateKey(bioPrivKey, NULL, NULL, NULL)))
{
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(privKey, NULL);
EVP_PKEY_free(privKey);
if (ctx)
{
if (EVP_PKEY_decrypt_init(ctx) > 0)
{
EVP_PKEY_CTX_ctrl_str(ctx, "rsa_padding_mode", "oaep");
EVP_PKEY_CTX_ctrl_str(ctx, "rsa_oaep_md", "sha256");
EVP_PKEY_CTX_ctrl_str(ctx, "rsa_mgf1_md", "sha256");
outLen = dataOutMax;
ret = EVP_PKEY_decrypt(ctx, dataOut, &outLen, dataIn, inDataLen);
if (ret > 0 && outLen > 0 && outLen <= dataOutMax)
{
// Success :-)
}
}
EVP_PKEY_CTX_free(ctx);
}
}
BIO_free_all(bioPrivKey);
}
You can add error handling for the failure cases using ERR_get_error().

Resources