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.
Related
I've got a sample code that is encrypting a message using PEM private key and decrypting it using PEM public key but at the end the decrypted result is empty.
const char * msg = "this is a test message";
//********************Encrypt*******************************
if ((pFile = fopen("private.pem", "rt")) &&
(rsa = PEM_read_RSAPrivateKey(pFile, NULL, passwd_callback, (void*)pcszPassphrase)))
{
fprintf(stderr, "Private key read.\n");
RSA_private_encrypt(strlen(msg), (unsigned char *)msg, encrypted, rsa, RSA_PKCS1_PADDING);
fclose(pFile);
}
//********************Decrypt*******************************
pFile = fopen("pubkey.pem", "rt");
if (rsa = PEM_read_RSAPublicKey(pFile, NULL, NULL, NULL))
{
RSA_public_decrypt(strlen((char *)encrypted), encrypted, decrypted, rsa, RSA_PKCS1_PADDING);
ERR_load_crypto_strings();
char * err = (char *)malloc(130);
ERR_error_string(ERR_get_error(), err);
fprintf(stderr, "Error decrypting message: %s\n", err);
}
as a result the output of RSA_public_decrypt is 1 but decrypted string is empty.
error message : Error decrypting message: error:0407008A:rsa routines:RSA_padding_check_PKCS1_type_1:invalid padding
Your input message msg is a null terminated string, but when you encrypt it to get encrypted buffer it will be binary buffer, strlen(encrypted) that you are passing to RSA_public_decrypt() will be invalid.
So change
RSA_public_decrypt(strlen((char *)encrypted), encrypted,
decrypted, rsa, RSA_PKCS1_PADDING);
to
RSA_public_decrypt(RSA_size(rsa), encrypted,
decrypted, rsa, RSA_PKCS1_PADDING);
I am trying to create an openssl aes encryption/decryption using php and c. I am able to encrypt text using php and openssl and this will output the encrypted string in a base64 string. I am trying to pass this base64 encoded string to a c program to decode it using openssl in c.
int decrypt(unsigned char *ciphertext,
int ciphertext_len,
unsigned char *key,
unsigned char *iv,
unsigned char *plaintext)
{
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
handleErrors();
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleErrors();
plaintext_len = len;
if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors();
plaintext_len += len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return plaintext_len;
}
Is there any method to decrypt the base64 string using openssl in c ? i am able to decrypt it using the command line something like
openssl enc -aes-256-cbc -d -in file.encrypted -nosalt -nopad -K 31323334353637383132333435363738 -iv 31323334353637383132333435363738
Thanks in advance
You are directly working on base64 encoded data.
First you need to base64 decode the data to get the actual encrypted data. Then you will apply AES decryption to get the decrypted data
BIO *b64, *bmem;
buffer = (char *)malloc(datalength);
b64 = BIO_new(BIO_f_base64());
if(b64 == NULL)
{
return NULL;
}
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new_mem_buf(input, length);
if(bmem == NULL)
{
return NULL;
}
bmem = BIO_push(b64, bmem);
if(bmem == NULL)
{
return NULL;
}
ret = BIO_read(bmem, buffer, length);
if(ret == 0)
{
return NULL;
}
BIO_free_all(bmem);
return buffer;
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;
}
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 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();