This is a c function I wrote to generate openssl rsa 4096 bit keys.
bool rsa_gen_keys()
{
int ret = 0;
RSA *rsa = NULL;
BIGNUM *bignum = NULL;
BIO *bio_private = NULL;
BIO *bio_public = NULL;
int bits = 4096;
unsigned long k = RSA_F4;
bignum = BN_new();
ret = BN_set_word(bignum,k);
if(ret != 1){
goto cleanup;
}
rsa = RSA_new();
ret = RSA_generate_key_ex(rsa, bits, bignum, NULL);
if(ret != 1){
goto cleanup;
}
// write rsa private key to file
bio_private = BIO_new_file("private_new.pem", "w+");
ret = PEM_write_bio_RSAPrivateKey(bio_private, rsa, NULL, NULL, 0, NULL, NULL);
BIO_flush(bio_private);
// write rsa public key to file
bio_public = BIO_new_file("public_new.pem", "w+");
ret = PEM_write_bio_RSAPublicKey(bio_public, rsa);
if(ret != 1){
goto cleanup;
}
BIO_flush(bio_public);
cleanup:
BIO_free_all(bio_private);
BIO_free_all(bio_public);
RSA_free(rsa);
BN_free(bignum);
return ret;
}
The keys generated by the above function seem to be missing something. When I try to use the public_new.pem file in another program, I get the following error:
140286309791384:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:701:Expecting: PUBLIC KEY
However, if I use the openssl command to generate the key files, the files work fine.
$openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:4096
I noticed the key sizes generated from the function and from the command line don't match. This is a clue, but what do I need to change in my function to fix this?
-rw-rw-r-- 1 3272 Feb 6 09:19 private_key.pem
-rw-rw-r-- 1 800 Feb 6 09:20 public_key.pem
-rw-rw-r-- 1 3243 Feb 6 10:43 private_new.pem
-rw-rw-r-- 1 775 Feb 6 10:43 public_new.pem
BTW, I tried the above with 2048 bits keys and I get the same result and same size mismatch
openssl genpkey is using PEM_write_bio_PrivateKey (PKCS#8) instead of PEM_write_bio_RSAPrivateKey (PKCS#1): https://github.com/openssl/openssl/blob/master/apps/genpkey.c#L161-L164.
You don't show how you generated public_key.pem, but it was probably written with PEM_write_bio_PUBKEY (X.509 SubjectPublicKeyInfo) vs PEM_write_bio_RSAPublicKey (PKCS#1).
From a PEM armor perspective:
PKCS#1 Public: BEGIN RSA PUBLIC KEY
X.509 SubjectPublicKeyInfo: BEGIN PUBLIC KEY
PKCS#1 Private: BEGIN RSA PRIVATE KEY
PKCS#8: BEGIN PRIVATE KEY
I realized I needed to use the format of the keys for PKCS#8 and X.509. So I switched to EVP functions to generate them. Here is a very simplified version of the code I ended up using (no error checking):
bool rsa_gen_keys() {
int ret = 0;
BIO *bio_private = NULL;
BIO *bio_public = NULL;
int bits = 4096;
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey = NULL;
// Get the context
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (!ctx)
goto cleanup;
// init keygen
if (EVP_PKEY_keygen_init(ctx) <= 0)
goto cleanup;
// set the bit size
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0)
goto cleanup;
/* Generate key */
if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
goto cleanup;
// write rsa private key to file
bio_private = BIO_new_file("private_new.pem", "w+");
ret = PEM_write_bio_PrivateKey(bio_private, pkey, NULL, NULL, 0, NULL, NULL);
if (ret != 1) {
goto cleanup;
}
BIO_flush(bio_private);
// write rsa public key to file
bio_public = BIO_new_file("public_new.pem", "w+");
//ret = PEM_write_bio_RSAPublicKey(bio_public, rsa);
ret = PEM_write_bio_PUBKEY(bio_public, pkey);
if (ret != 1) {
goto cleanup;
}
BIO_flush(bio_public);
cleanup:
if(bio_private) BIO_free_all(bio_private);
if(bio_public) BIO_free_all(bio_public);
if(pkey) EVP_PKEY_free(pkey);
return ret;
}
Related
I am creating an RSA key pair with the following code (C and OpenSSL library):
void rsa_gen_keys_ca() {
RSA *keypair = NULL;
unsigned char *pub_key = NULL;
BIGNUM *bne = NULL;
unsigned long e = RSA_F4;
int success = 0;
bne = BN_new();
success = BN_set_word(bne, e);
if (!success) {
errx(1, "\nrsa_gen_keys_ca failed at BN_set_word result.");
goto free_all;
}
keypair = RSA_new();
success = RSA_generate_key_ex(keypair, RSA_KEY_SIZE, bne, NULL);
if (!success) {
errx(1, "\nrsa_gen_keys_ca failed at RSA_generate_key_ex result.");
goto free_all;
}
success = i2d_RSAPublicKey(keypair, &pub_key);
if (success < 0) {
errx(1, "\nrsa_gen_keys_ca failed at i2d_RSAPublicKey result.");
goto free_all;
}
printf("==========RSA Public Key successfully extracted: %s", pub_key);
free_all:
RSA_free(keypair);
BN_free(bne);
}
When I run it, I am receiving the following warning:
random: uninitialized urandom read (32 bytes read)
I think leaving the code with this warning would be a security concern, since in the past I read something regarding the urandom generator.
Can anyone explain this warning and how to avoid it?
I am trying to sign a message with a RSA private key. I read a private key to pkey and then sign a string as what says on openssl wiki, but failed in the final step. It always returns 0 on the line commented in program, which means sign failed. Could anyone helps me find out what’s wrong?
void main() {
EVP_MD_CTX * mdctx ;
EVP_PKEY * pkey ;
char dmessage[20] = "The messages";
int ret = 0;
FILE * fp;
unsigned char * sig = NULL;
size_t * slen = malloc(sizeof(size_t));
fp = fopen ("privkey.pem", "r");
if (fp == NULL) exit (1);
pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
fclose (fp);
if (pkey == NULL) {
ERR_print_errors_fp (stderr);
exit (1);
}
if(!(mdctx = EVP_MD_CTX_create())) goto err;
if(1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, pkey)) goto err;
if(1 != EVP_DigestSignUpdate(mdctx, dmessage, 12)) goto err;
if(1 != EVP_DigestSignFinal(mdctx, NULL, slen)) goto err;
if(!(sig = OPENSSL_malloc(sizeof(unsigned char) * (int)(*slen)))) goto err;
if(1 != (ret = EVP_DigestSignFinal(mdctx, sig, slen))) goto err;//*****it return 0 here,which means sign failed
ret = 1;
err:
if(ret != 1)
{
printf("%d somthing wrong\n",ret);
}
/* Clean up */
if(sig && !ret) OPENSSL_free(sig);
if(mdctx) EVP_MD_CTX_destroy(mdctx);
return;
}
Thanks a lot!
I’m using openssl 1.0.1j on linux mint 17, and the private key is generated by
openssl genrsa -out privkey.pem 256
Your key is way too small, that's bits not bytes. Try again with a good secure key size that can hold the hash and PKCS#1 padding. I would recommend at least 2048 bits instead of the 256 bits that you generated using the OpenSSL command line.
See keylength.com for more information about key sizes. Note that RSA requires a key size a lot larger than those required for symmetric algorithms such as AES.
I have a problem with encrypting a string with my generated RSA pubkey. This key is stored in an unsigned char array. Here is an example of that key
char *testkey = "-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEA3InRLxdx25R6eA4PyXcYzKyFiJULS3ypqlETztAW488XkEM263vJ\n"
"SY5xpfwph9thcsjYUI3H60qxaTRVhNxbzbsG0ELkEudm4cLMn2oVpphT4cB3zx6V\n"
"az1cuzIfmL34M8YiRQw6MvdaDJS34y15dXDm0BSXF7sanZaYrHvu84j5mQVK0OWq\n"
"kvpVs+J55xul/IQsSEWr94HjPupdDYzGXsEiQ7p5cNrvKgjGKqKV177EYROVVgVp\n"
"gAWm0G6aDrfDLKqsXo8RXj4dyyuZqoL2e7Fa46Gz4I+tb2SWkEwLGpqBe/CUDzDh\n"
"9aLhaTijDQhcaR5+u88XNbarckKU96wiiQIDAQAB\n"
"-----END RSA PUBLIC KEY-----\0";
I have currently written this code to do the encryption
RSA *rsa = NULL;
BIO *key_bio;
static const int bit = 2048;
key_bio = BIO_new_mem_buf(testkey, -1);
if(key_bio == NULL){
printf("No key bio \n");
exit(1);
}
rsa = PEM_read_bio_PUBKEY(key_bio, NULL, NULL, NULL);
if(rsa == NULL){
fprintf(stderr, "Error loading RSA Public Key File.\n");
ERR_print_errors_fp(stderr);
exit(1);
}
// Alloc the encrypted buffer
e->enckey = calloc(1, 2048);
int res = RSA_public_encrypt(strlen((char*)e->key->key), e->key->key, e->enckey, rsa, RSA_PKCS1_PADDING);
if(res == -1){
printf("Failed to encrypt AES key \n");
printf("Error: strerror(errno)\n");
}
However, this do not work. I think it is something about the format of the key, however I have not managed to find out how to convert it, and use the RSA struct to do the encryption.
Any ideas?
It seems to be working fine now, using this insead:
PEM_read_bio_RSAPublicKey(key_bio, &rsa, 0, NULL);
The reason for this probably have something to do with the formats of the KEY.
I am trying out a small example program to decrypt a message that has been signed and then encrypted using openSSL. It works well in the command line. However upon trying out the code after modifying the code in the 'demos' folder of OpenSSL, the decryption fails
Here is the decryption code:
int decrypt_smime(){
BIO *in = NULL, *out = NULL, *tbio = NULL;
X509 *rcert = NULL;
EVP_PKEY *rkey = NULL;
//PKCS7 *cms = NULL;
CMS_ContentInfo *cms = NULL;
int ret = 1;
int flags = CMS_STREAM;
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
printf("decrypt...\n");
/* Read in recipient certificate and private key */
tbio = BIO_new_file("signer.pem", "r");
if (!tbio)
goto err;
rcert = PEM_read_bio_X509(tbio, NULL, 0, NULL);
BIO_reset(tbio);
rkey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL);
if (!rcert || !rkey)
goto err;
printf("decrypt...\n");
/* Open S/MIME message to decrypt */
in = BIO_new_file("smencsign.txt", "r");
if (!in)
goto err;
printf("keys read...\n");
/* Parse message */
cms = SMIME_read_CMS(in, NULL); //here is the problem I think
if (!cms)
goto err;
printf("keys read...\n");
out = BIO_new_file("decout.txt", "w");
if (!out)
goto err;
/* Decrypt S/MIME message */
if (!CMS_decrypt(cms, rkey, rcert, NULL, out, flags))
goto err;
ret = 0;
err:
if (ret)
{
fprintf(stderr, "Error Decrypting Data\n");
ERR_print_errors_fp(stderr);
}
if (cms)
//PKCS7_free(cms);
CMS_ContentInfo_free(cms);
if (rcert)
X509_free(rcert);
if (rkey)
EVP_PKEY_free(rkey);
if (in)
BIO_free(in);
if (out)
BIO_free(out);
if (tbio)
BIO_free(tbio);
return ret;
}
The error I get is:
Error Verifying Data
*3074258568:error:0D0D40D1:asn1 encoding routines:SMIME_read_ASN1:no content type:asn_mime.c:451:*
The commands on openssl that worked:
openssl cms -sign -in encr.txt -signer signer.pem -text | openssl cms -encrypt -out smencsign.txt signer.pem
openssl smime -decrypt -in smencsign.txt -recip signer.pem -inkey signer.pem
So clearly openssl uses 'cms' utility to sign and encrypt but seems to use 'smime' utility for decryption. What is then the code equivalent?
Try to add the following line:
OpenSSL_add_all_ciphers();
I am facing a problem in RSA decryption using OpenSSL Library (EVP api).
Here is my code for key generation
#include <stdio.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#define SECFILE "sec.pem"
#define PUBFILE "pub.pem"
int main()
{
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey = NULL;
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
FILE *fp;
if (!ctx)
{
/* Error occurred */
perror("Error in CTX \n");
}
if (EVP_PKEY_keygen_init(ctx) <= 0)
{
/* Error */
perror("Error in EVP_PKEY_keygen_init \n");
}
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) <= 0)
{
/* Error */
perror("Error in EVP_PKEY_CTX_set_rsa_keygen_bits \n");
}
/* Generate key */
if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
{
/* Error */
perror("Error in EVP_PKEY_keygen \n");
}
fp = fopen(SECFILE, "w");
PEM_write_PrivateKey(fp, pkey, NULL,NULL, 0,0, NULL);
fclose(fp);
fp = fopen(PUBFILE, "w");
PEM_write_PUBKEY(fp,pkey);
fclose(fp);
return 0;
}
For encryption :
I used this link
For decryption :
int do_evp_open(FILE *rsa_pkey_file, FILE *in_file, FILE *out_file)
{
int retval = 0;
RSA *rsa_pkey = NULL;
EVP_PKEY *pkey = EVP_PKEY_new();
EVP_CIPHER_CTX ctx;
unsigned char buffer[4096];
unsigned char buffer_out[4096 + EVP_MAX_IV_LENGTH];
size_t len;
int len_out;
unsigned char *ek;
int eklen ;
uint32_t eklen_n;
unsigned char iv[EVP_MAX_IV_LENGTH] = {122,205,106,192,4,183,69,176,84,28,214,226,220,140,86,174};
/// Read RSA Private Key
if (PEM_read_RSAPrivateKey(rsa_pkey_file, &rsa_pkey, NULL, NULL) == NULL)
{
fprintf(stderr, "Error loading RSA Private Key File.\n");
ERR_print_errors_fp(stderr);
retval = -2;
goto out;
}
/// Assign RSA key to EVP key
if (!EVP_PKEY_assign_RSA(pkey, rsa_pkey))
{
fprintf(stderr, "EVP_PKEY_assign_RSA: failed.\n");
retval = -3;
goto out;
}
EVP_CIPHER_CTX_init(&ctx);
ek = malloc( EVP_PKEY_size(pkey));
if (!EVP_OpenInit(&ctx, EVP_aes_128_cbc(), ek, eklen, iv,pkey))
{
fprintf(stderr, "EVP_OpenInit: failed.\n");
ERR_print_errors_fp(stderr); /// Prints error of occured in Openssl
retval = -3;
goto out_free;
}
while ((len = fread(buffer, 1, sizeof buffer, in_file)) > 0)
{
if (!EVP_OpenUpdate(&ctx, buffer_out, &len_out, buffer, len))
{
fprintf(stderr, "EVP_OpenUpdate: failed.\n");
retval = 3;
goto out_free;
}
if (fwrite(buffer_out, len_out, 1, out_file) != 1)
{
perror("output file");
retval = -5;
goto out_free;
}
}
if (ferror(in_file))
{
perror("input file");
retval = -4;
goto out_free;
}
if (!EVP_OpenFinal(&ctx, buffer_out, &len_out))
{
fprintf(stderr, "EVP_OpenFinal: failed.\n");
retval = - 3;
goto out_free;
}
if (fwrite(buffer_out, len_out, 1, out_file) != 1)
{
perror("output file");
retval = -5;
goto out_free;
}
out_free:
EVP_PKEY_free(pkey);
free(ek);
out:
return retval;
}
My Private and public keys are :
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDj+g6m7KY5zK8z
FZ/MKFySr1ZB+n0b3GjhMUcUDn5S9N7oyoCzkOVVa7gl0jTI8dwCFVAy8693Rq+i
AT7dUTLViT9V8GkX9r0yFcppt5uc2YgI5aOOTvmQKQe08FSZ7QbviEL25MNnBfB1
wpd+mJN3nb0hkeo7x2IZD/ZVfs+TmOG9mHbQR8b1XcxEDoLx3aX1J8Eix1pN7YK6
nDAFm984Ho+PCz1aGl/TnDl3b90X5HXGHiD6uNDHokZ+th8B6AeFFRlkWlQd0w2R
e/EEZ36p4TQOHSc3sSUw6pen0N8YmBNZksBEr1vsZvYkKtRKCfy0fXtL4iqKzcgJ
ocos+Z6nAgMBAAECggEBALlWDdlYpF5y76/JEaso2PGLR8XFvTYMPttsc0tz6PDK
D/oSvwS8dCS4uPFObgk6ztCGwTda8rg2KAy9lHzaSUheFrZoBxgrSG5SVscRNJoU
IsqQ3iGQRMUVBiXsB+tHTg8nqMENA2pa4rzpoL2Tjrd87kg/VryYgEC9wFaLDHgB
FaXJJlaeuTBQXV7Ga9pg+KF1Kv91/q3T62Um0ggSajFpX15x6sLIo5EWm0DGksn0
chQeiEs33e8fHil95g0nXK+hXOMnMvbAln/eOCGktO4JnPTjicAA7iKliCsLiaeZ
t5Czscv/8AVlBAISGJcE8ASM+AbalXtnoOK6W+dv0gkCgYEA/cPi7U8bJ0tlvxF3
3vc8V7uzuAgKtOKQet1spQtAoi343ZoSyFHQEwO5PMmtB56/mk3+/mDpoKTQo0oK
f5COIlzW+PIMQCroalpJe+ZcY/PS8SPoOY39yiX4WQchgac01R0Qf6XDAOfyfZ9d
MZTtDgpkx/oyfRTzxI7D9SZUqZ0CgYEA5fwH5uwmc0fsIw3tl3pHHOK9g78Rs6XE
0feXplBCzx+qaEtAK1Jp1nMX/PmN575i3UN3dii9YV8v9geTf+hytpQd/TbrvsSY
Py9j95XYN75Z5TAsWnZbZr/gTdZSz0yObb/9GzxBipp+EHCEOSS0RF8u9XHlM9bC
0agB4VZKqBMCgYByIjxaR44K6lpkyVKEseYt/3ohd1x5Zr1cxWIsCReU2eBoqvdv
qXxQUQhrUrnEB55dpF7fwm7Rlc9Q4eg+36FNyzvU0+i2o5XM37bVRxKe0fc6BdBY
sohG9zTvtclYKwAUKfJVtxQxwCDMZ3Te7ACCpCIX32v93gKVkTCJfift8QKBgHZ9
PAEAZ+r7AjEpSuDBMgQy2ZsYBOG+pUHcQzh/n3wg/2XOZ1gqlLbVA2XlmPPtxffj
e5fX84JITWh/jMHYm8lvVGgSNLFLjnj3TJTRkd1eZ+hJwoA0/HBaqRDRPEbrVXI7
+QZgLBBh+lMz9RuPyoRzWblBHepwWl00JwvWro4bAoGBAKFAXVzbx74JM6wzr9H5
TusTwOM5mf/I1TkCq1Dd5n1vDVfrkNokZ2LfJWqiQHLZj2rGxSQSVjIsVaBmEDTZ
ob8duUsbkYQe4dToHFHcBO+akBtULC4HWv/D4pPVoyAE7WJBJBw0vl1sA15kiXBu
HBXffOzN/Erqvp90HLtefpMp
-----END PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4/oOpuymOcyvMxWfzChc
kq9WQfp9G9xo4TFHFA5+UvTe6MqAs5DlVWu4JdI0yPHcAhVQMvOvd0avogE+3VEy
1Yk/VfBpF/a9MhXKabebnNmICOWjjk75kCkHtPBUme0G74hC9uTDZwXwdcKXfpiT
d529IZHqO8diGQ/2VX7Pk5jhvZh20EfG9V3MRA6C8d2l9SfBIsdaTe2CupwwBZvf
OB6Pjws9Whpf05w5d2/dF+R1xh4g+rjQx6JGfrYfAegHhRUZZFpUHdMNkXvxBGd+
qeE0Dh0nN7ElMOqXp9DfGJgTWZLARK9b7Gb2JCrUSgn8tH17S+Iqis3ICaHKLPme
pwIDAQAB
-----END PUBLIC KEY-----
The error I am getting While EVP_OpenInit is :
140004942804648:error:0407106B:lib(4):func(113):reason(107):rsa_pk1.c:190: 140004942804648:error:04065072:lib(4):func(101):reason(114):rsa_eay.c:594
Any help would be appreciated.
Thanks,
Pawan
Are you absolutely sure, that you have initialized variable eklen?
I see you have used EVP_OpenInit(), EVP_OpenUpdate() and EVP_OpenFinal() for decryption. So I'm going to assume that you've used EVP_SealInit(), EVP_SealUpdate() and EVP_SealFinal() functions for encryption.
So if you have used EVP_SealInit(), you must have passed the address (pointer) to eklen (please check man EVP_SealInit) where the variable gets a value assigned. In analogous to that the eklen variable that you are passing to EVP_OpenInit() must contain a valid key length.
The following is the description from man EVP_OpenInit.
EVP_OpenInit() initializes a cipher context ctx for decryption with cipher type. It decrypts the encrypted symmetric key of length ekl bytes passed in the ek parameter using the private key priv. The IV is supplied in the iv parameter.
I'm not sure here, but I guess you need to use EVP_PKEY_size() function in this case to get the length of the key.
For example (in your decryption code):
....
..
.
EVP_CIPHER_CTX_init(&ctx);
ek = malloc( EVP_PKEY_size(pkey));
/* Add the following line */
eklen = EVP_PKEY_size(pkey);
if (!EVP_OpenInit(&ctx, EVP_aes_128_cbc(), ek, eklen, iv,pkey))
{
fprintf(stderr, "EVP_OpenInit: failed.\n");
...
..
.