I have the following code to decrypt some AEAD encrypted password:
int aead_decrypt(char *cipher_password, int len_cipher_password, char *tag, char *key, char *iv, int len_iv, char **plaintext_password) {
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
// Cipher_password len always greater or equal to plaintext
*plaintext_password = (unsigned char *)malloc(len_cipher_password);
if(*plaintext_password == 0) {
fprintf(stderr, "malloc() failure\n");
free(*plaintext_password);
return -1;
}
if(!(ctx = EVP_CIPHER_CTX_new())) {
fprintf(stderr, "EVP_CIPHER_CTX_new() failure\n");
ERR_print_errors_fp(stderr);
return -1;
}
if(!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) {
fprintf(stderr, "EVP_DecryptInit_ex() failure\n");
ERR_print_errors_fp(stderr);
return -1;
}
if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
fprintf(stderr, "EVP_DecryptInit_ex() failure\n");
ERR_print_errors_fp(stderr);
return -1;
}
if(!EVP_DecryptUpdate(ctx, *plaintext_password, &len, cipher_password, len_cipher_password)) {
//if(!EVP_DecryptUpdate(ctx, *plaintext_password, &len, padded_cipher_password, len_padded_cipher_password)) {
fprintf(stderr, "EVP_DecryptUpdate() failure\n");
ERR_print_errors_fp(stderr);
return -1;
}
if(1!=EVP_DecryptFinal_ex(ctx, *plaintext_password+len, &len)) {
fprintf(stderr, "EVP_DecryptFinal_ex() failure\n");
ERR_print_errors_fp(stderr);
ERR_print_errors_fp(stdout);
return -1;
}
plaintext_len += len;
(*plaintext_password)[plaintext_len] = '\0';
EVP_CIPHER_CTX_free(ctx);
return 1;
}
My problem is that the EVP_DecryptFinal_ex() function always fail but without printing any errors.
My plaintext_password comes out decrypted but with 16 bytes of garbage at the end because the EVP_DecryptUpdate() function doesn't return the good plaintext_password_len.
I thought at first it was because of padding, my cipher_password is often 24-25 bytes long, so i tried adding some as we can see in the different comments but it did not worked out.
(Also i know i pass some parameters i don't use but that's not what's important here).
I don't know where the problem could be and i'm not that familiar with the OpenSSL library.
Got it, i was actually confusing aad and tag values. In Authenticated encryption the tag value is always generated (can't be null). In my example it was the default size: 16 bytes. Tag value is then appended to the cipher data. You can use it to authenticate your decrypted data.
Here is my fixed code:
int aead_decrypt(char *cipher_password, int len_cipher_password, char *key, char *iv, int len_iv, char **plaintext_password) {
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
// The tag is appended at the end of the cipher data
int tag_offset = len_cipher_password-16;
// Cipher_password len always greater or equal to plaintext
*plaintext_password = (unsigned char *)malloc(len_cipher_password);
if(*plaintext_password == 0) {
fprintf(stderr, "malloc() failure\n");
free(*plaintext_password);
return -1;
}
if(!(ctx = EVP_CIPHER_CTX_new())) {
fprintf(stderr, "EVP_CIPHER_CTX_new() failure\n");
ERR_print_errors_fp(stderr);
return -1;
}
if(!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) {
fprintf(stderr, "EVP_DecryptInit_ex() failure\n");
ERR_print_errors_fp(stderr);
return -1;
}
if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
fprintf(stderr, "EVP_DecryptInit_ex() failure\n");
ERR_print_errors_fp(stderr);
return -1;
}
// Set the expected tag value for authenticated data
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, cipher_password+tag_offset)) {
fprintf(stderr, "EVP_CIPHER_CTX_ctrl() failure\n");
ERR_print_errors_fp(stderr);
return -1;
}
if(!EVP_DecryptUpdate(ctx, *plaintext_password, &len, cipher_password, tag_offset)) {
fprintf(stderr, "EVP_DecryptUpdate() failure\n");
ERR_print_errors_fp(stderr);
return -1;
}
plaintext_len = len;
if(1!=EVP_DecryptFinal_ex(ctx, *plaintext_password+len, &len)) {
fprintf(stderr, "EVP_DecryptFinal_ex() failure\n");
ERR_print_errors_fp(stderr);
ERR_print_errors_fp(stdout);
}
plaintext_len += len;
(*plaintext_password)[plaintext_len] = '\0';
EVP_CIPHER_CTX_free(ctx);
return 1;}
Related
So I am writing an archiver program to also encrypt-decrypt files sing block cipher. When decrpting from a archive file, and I have checked, in situations where the remaining btes of the fle before EOF is smaller than the read buffer some garbage values are written to the file. I am using OpenSSL's EVP API with C language. Any ideas what is wrong? You can find the code for AES 256 bit decryption below.
int TARIM_decrypt_aes256(FILE* infile, FILE* outfile, unsigned char* key, unsigned char* iv, unsigned long long int fileLoc, unsigned long int fileSize)
{
if (infile == NULL || outfile == NULL || key == NULL || iv == NULL)
{
printf("(ERROR) decrypt_aes256: One/more parameters are NULL pointers\n");
return 1;
}
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
{
printf("(ERROR) decrypt_aes256: Failed to generate cipher context. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
return 1;
}
EVP_CIPHER* cipher = EVP_CIPHER_fetch(NULL, "AES-256-CBC", NULL);
if (cipher == NULL)
{
printf("(ERROR) decrypt_aes256: Failed to fetch cipher. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_free(ctx);
return 1;
}
// Explicitly Enable Padding
EVP_CIPHER_CTX_set_padding(ctx, 1);
int num_read = 0;
int block_size = EVP_CIPHER_block_size(cipher);
int inLen = 1024;
int outLen = inLen + block_size;
unsigned char inbuffer[inLen], outbuffer[outLen]; // Allow space for additional block in outbuffer
if (!EVP_CipherInit_ex2(ctx, cipher, NULL, NULL, 0, NULL))
{
printf("(ERROR) decrypt_aes256: Failed to Initiate Cipher. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_free(cipher);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
// Assert size of key and iv
OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 32);
OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);
// Set key and iv
if (!EVP_CipherInit_ex2(ctx, NULL, key, iv, 0, NULL))
{
printf("(ERROR) decrypt_aes256: Failed to set key and iv. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_free(cipher);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
// Seek to File Location
fseek(infile, fileLoc, SEEK_SET);
// Loop until bytes read
unsigned long long int b_count = 0;
while (1)
{
num_read = fread(inbuffer, sizeof(unsigned char), inLen, infile);
b_count += num_read;
if (!EVP_CipherUpdate(ctx, outbuffer, &outLen, inbuffer, num_read))
{
printf("(ERROR) decrypt_aes256: Failed to pass bytes to the cipher. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_free(cipher);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
fwrite(outbuffer, sizeof(unsigned char), outLen, outfile);
// EOF
if (b_count >= fileSize || num_read < inLen)
{ break; }
}
// Cipher Final block with padding
if (!EVP_CipherFinal_ex(ctx, outbuffer, &outLen))
{
printf("(ERROR) decrypt_aes256: Failed to pass bytes from final block to cipher. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_free(cipher);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
fwrite(outbuffer, sizeof(unsigned char), outLen, outfile);
// Clean up
EVP_CIPHER_free(cipher);
EVP_CIPHER_CTX_free(ctx);
return 0;
}
I tried changing the inLen variable to block_size but that gives me OpenSSL's bad decrypt error.
int inLen = block_size;
The Error comes out like this dring decryption:
(ERROR) decrypt_aes256: Failed to pass bytes from final block to cipher. OpenSSL: error:1C800064:Provider routines::bad decrypt
[Edit]
I did find a weird behavior of my program. So the program can encrypt files together into a single file. The files are not padded to differentiate to save space, instead I keep the record of the file size and figure out the start byte position in the code using their sizes and extract them. The only files that give me trouble when using inLen = block_size were the files who were a multiple for 'block_size' and only when they were clustered in a single file. Here is the code that figures out the start byte of a file:
// Get Relative Location
unsigned long long int relativeLoc = 0;
for (long long int itr = 0; itr < option_num; itr++) {
if (block_size == 0 || fArray[itr].fsize == 0) {
relativeLoc += fArray[itr].fsize;
} else {
// Adjust for padding
relativeLoc += ((fArray[itr].fsize / block_size) + 1) * block_size;
}
}
For industrial purposes, I want to decrypt an AES-encrypted message with an RSA-encrypted key in C. At first, I thought doing it step-by-step by first, using OpenSSL libcrypto library, by first RSA decoding the key then AES decoding the data.
I have found out that EVP tools were commonly seen as a better way to do this since it actually does what the low-levels functions do but correctly.
Here is what I see the flow of the program :
Initialize OpenSSL;
Read and store the RSA private key;
Initialize the decryption by specifying the decryption algorithm (AES) and the private key;
Update the decryption by giving the key, the data, the key and their length
Finally decrypt the data and return it.
I have been a lot confused by the fact that so far we do not intend to use any IV or ADD (although IV might come up later in the project). I have followed this guide it is not very clear and does not fit the way I use EVP.
So here is my actual code :
#include <openssl/evp.h>
#include <openssl/conf.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include "openssl\applink.c"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
const char PRIVATE_KEY_PATH[] = "C:/Users/Local_user/privateKey.pem";
EVP_PKEY* initializePrivateKey(void)
{
FILE* privateKeyfile;
if ((privateKeyfile = fopen(PRIVATE_KEY_PATH, "r")) == NULL) // Check PEM file opening
{
perror("Error while trying to access to private key.\n");
return NULL;
}
RSA *rsaPrivateKey = RSA_new();
EVP_PKEY *privateKey = EVP_PKEY_new();
if ((rsaPrivateKey = PEM_read_RSAPrivateKey(privateKeyfile, &rsaPrivateKey, NULL, NULL)) == NULL) // Check PEM file reading
{
fprintf(stderr, "Error loading RSA Private Key File.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
if (!EVP_PKEY_assign_RSA(privateKey, rsaPrivateKey))
{
fprintf(stderr, "Error when initializing EVP private key.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
return privateKey;
}
const uint8_t* decodeWrappingKey(uint8_t const* data, const size_t data_len, uint8_t const* wrappingKey, const size_t wrappingKey_len)
{
// Start Decryption
EVP_CIPHER_CTX *ctx;
if (!(ctx = EVP_CIPHER_CTX_new())) // Initialize context
{
fprintf(stderr, "Error when initializing context.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
EVP_PKEY *privateKey = initializePrivateKey();
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, privateKey, NULL)) // Initialize decryption
{
fprintf(stderr, "Error when initializing decryption.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
uint8_t* res;
if ((res = calloc(data_len, sizeof(uint8_t))) == NULL) // Check memory allocating
{
perror("Memory allocating error ");
return NULL;
}
puts("Initialization done. Decoding..\n");
size_t res_len = 0;
if (1 != EVP_DecryptUpdate(ctx, res, &res_len, data, data_len))
{
fprintf(stderr, "Error when preparing decryption.\n");
ERR_print_errors_fp(stderr);
}
if (1 != EVP_DecryptFinal_ex(ctx, res, &res_len))
{
fprintf(stderr, "Error when decrypting.\n");
ERR_print_errors_fp(stderr);
}
return res;
}
void hexToBytes(uint8_t *des, char const *source, const size_t size) {
for (int i = 0; i < size - 1; i += 2)
sscanf(source + i, "%02x", des + (i / 2));
}
int main(void) {
char const *strWrap = "5f82c48f85054ef6a3b2621819dd0e969030c79cc00deb89........";
char const *strData = "ca1518d44716e3a4588af741982f29ad0a3e7a8d67.....";
uint8_t *wrap = calloc(strlen(strWrap), sizeof(uint8_t));
hexToBytes(wrap, strWrap, strlen(strWrap)); // Converts string to raw data
uint8_t *data = calloc(strlen(strData), sizeof(uint8_t));
hexToBytes(data, strData, strlen(strData));
/* Load the human readable error strings for libcrypto */
ERR_load_crypto_strings();
/* Load all digest and cipher algorithms */
OpenSSL_add_all_algorithms();
/* Load config file, and other important initialisation */
OPENSSL_config(NULL);
const uint8_t *res = decodeWrappingKey(data, strlen(strData) / 2, wrap, strlen(strWrap) / 2);
if (res == NULL)
return 1;
return 0;
}
My output is the following one :
Initialization done. Decoding..
Error when preparing decryption.
Error when decrypting.
Obviously it fails when updating and finalising the decryption but I can't figure out why and the ERR_print_errors_fp(stderr); which had always worked for me so far seems to be mute.
Here is a complete working example of how you can encrypt a key using RSA, and encrypt a message using that key using AES, followed by the subsequent decryption of those things. It assumes AES-256-CBC is being used. If you want to use AES-256-GCM instead then you will need to make some changes to get and set the tag (ask me if you need some pointers on how to do this). It also assumes that the RSA encryption is done with PKCS#1 padding (which is all that the EVP_Seal* APIs support). If you need some other kind of padding then you will need to use a different method. Finally it assumes you are using OpenSSL 1.1.0. If you are using 1.0.2 then some changes will probably be necessary (at least you will need to explicitly init and de-init the library - that isn't required in 1.1.0).
The code reads the RSA private and public keys from files called privkey.pem and pubkey.pem which are in the current working directory. I generated these files like this:
openssl genrsa -out privkey.pem 2048
openssl rsa -in privkey.pem -pubout -out pubkey.pem
I've tested this on Linux only. The code is as follows:
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int envelope_seal(EVP_PKEY *pub_key, unsigned char *plaintext,
int plaintext_len, unsigned char **encrypted_key,
int *encrypted_key_len, unsigned char **iv,
int *iv_len, unsigned char **ciphertext,
int *ciphertext_len)
{
EVP_CIPHER_CTX *ctx;
int len, ret = 0;
const EVP_CIPHER *type = EVP_aes_256_cbc();
unsigned char *tmpiv = NULL, *tmpenc_key = NULL, *tmpctxt = NULL;
if((ctx = EVP_CIPHER_CTX_new()) == NULL)
return 0;
*iv_len = EVP_CIPHER_iv_length(type);
if ((tmpiv = malloc(*iv_len)) == NULL)
goto err;
if ((tmpenc_key = malloc(EVP_PKEY_size(pub_key))) == NULL)
goto err;
if ((tmpctxt = malloc(plaintext_len + EVP_CIPHER_block_size(type)))
== NULL)
goto err;
if(EVP_SealInit(ctx, type, &tmpenc_key, encrypted_key_len, tmpiv, &pub_key,
1) != 1)
goto err;
if(EVP_SealUpdate(ctx, tmpctxt, &len, plaintext, plaintext_len) != 1)
goto err;
*ciphertext_len = len;
if(EVP_SealFinal(ctx, tmpctxt + len, &len) != 1)
goto err;
*ciphertext_len += len;
*iv = tmpiv;
*encrypted_key = tmpenc_key;
*ciphertext = tmpctxt;
tmpiv = NULL;
tmpenc_key = NULL;
tmpctxt = NULL;
ret = 1;
err:
EVP_CIPHER_CTX_free(ctx);
free(tmpiv);
free(tmpenc_key);
free(tmpctxt);
return ret;
}
int envelope_open(EVP_PKEY *priv_key, unsigned char *ciphertext,
int ciphertext_len, unsigned char *encrypted_key,
int encrypted_key_len, unsigned char *iv,
unsigned char **plaintext, int *plaintext_len)
{
EVP_CIPHER_CTX *ctx;
int len, ret = 0;
unsigned char *tmpptxt = NULL;
if((ctx = EVP_CIPHER_CTX_new()) == NULL)
return 0;
if ((tmpptxt = malloc(ciphertext_len)) == NULL)
goto err;
if(EVP_OpenInit(ctx, EVP_aes_256_cbc(), encrypted_key, encrypted_key_len,
iv, priv_key) != 1)
return 0;
if(EVP_OpenUpdate(ctx, tmpptxt, &len, ciphertext, ciphertext_len) != 1)
return 0;
*plaintext_len = len;
if(EVP_OpenFinal(ctx, tmpptxt + len, &len) != 1)
return 0;
*plaintext_len += len;
*plaintext = tmpptxt;
tmpptxt = NULL;
ret = 1;
err:
EVP_CIPHER_CTX_free(ctx);
free(tmpptxt);
return ret;
}
int main(void)
{
EVP_PKEY *pubkey = NULL, *privkey = NULL;
FILE *pubkeyfile, *privkeyfile;
int ret = 1;
unsigned char *iv = NULL, *message = "Hello World!\n";
unsigned char *enc_key = NULL, *ciphertext = NULL, *plaintext = NULL;
int iv_len = 0, enc_key_len = 0, ciphertext_len = 0, plaintext_len = 0, i;
if ((pubkeyfile = fopen("pubkey.pem", "r")) == NULL) {
printf("Failed to open public key for reading\n");
goto err;
}
if ((pubkey = PEM_read_PUBKEY(pubkeyfile, &pubkey, NULL, NULL)) == NULL) {
fclose(pubkeyfile);
goto err;
}
fclose(pubkeyfile);
if ((privkeyfile = fopen("privkey.pem", "r")) == NULL) {
printf("Failed to open private key for reading\n");
goto err;
}
if ((privkey = PEM_read_PrivateKey(privkeyfile, &privkey, NULL, NULL))
== NULL) {
fclose(privkeyfile);
goto err;
}
fclose(privkeyfile);
if (!envelope_seal(pubkey, message, strlen(message), &enc_key, &enc_key_len,
&iv, &iv_len, &ciphertext, &ciphertext_len))
goto err;
printf("Ciphertext:\n");
for (i = 0; i < ciphertext_len; i++)
printf("%02x", ciphertext[i]);
printf("\n");
printf("Encrypted Key:\n");
for (i = 0; i < enc_key_len; i++)
printf("%02x", enc_key[i]);
printf("\n");
printf("IV:\n");
for (i = 0; i < iv_len; i++)
printf("%02x", iv[i]);
printf("\n");
if (!envelope_open(privkey, ciphertext, ciphertext_len, enc_key,
enc_key_len, iv, &plaintext, &plaintext_len))
goto err;
plaintext[plaintext_len] = '\0';
printf("Plaintext: %s\n", plaintext);
ret = 0;
err:
if (ret != 0) {
printf("Error\n");
ERR_print_errors_fp(stdout);
}
EVP_PKEY_free(pubkey);
EVP_PKEY_free(privkey);
free(iv);
free(enc_key);
free(ciphertext);
free(plaintext);
return ret;
}
Your key is encrypted with RSA, so you will decrypt it with RSA APIs like RSA_private_decrypt first, not with EVP* APIs.
Once you get key decrypted you need to use it with (EVP*) APIs to decrypt the data with AES.
I'm creating a simple utility to encrypt and decrypt files using a key pair. I'm on Windows and coding against the 1.1.0 version of Openssl. I can load the key pair and encrypt the file fine, but when I try to decrypt EVP_PKEY_decrypt always returns -1. I traced this to the rsa padding check functions and they are returning -1 but I can't figure out why. I've tried changing the padding from RSA_PKCS1_OAEP_PADDING to RSA_PKCS1_PADDING and still have the same problem. Any insight would be appreciated, here are my encrypt and decrypt functions:
#define FILE_BUFFER_LENGTH 1
#define ENC_BUFFER_LENGTH 2048
int encryptfile(EVP_PKEY *key, FILE *srcfp, FILE *tgtfp) {
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key, NULL);
char *inbuf;
unsigned char *outbuf;
size_t in_len = 0;
size_t out_len = ENC_BUFFER_LENGTH;
int x;
inbuf = (char*)malloc(sizeof(char)*FILE_BUFFER_LENGTH+1);
outbuf = (char*)malloc(sizeof(char)*ENC_BUFFER_LENGTH+1);
if (ctx == NULL) {
fprintf(stderr, "Error while creating encryption context.\n");
return 0;
}
if (EVP_PKEY_encrypt_init(ctx) <= 0) {
fprintf(stderr, "Error while initializing encryption context.\n");
return 0;
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) {
fprintf(stderr, "Error while setting encryption padding.\n");
return 0;
}
while (1) {
in_len = fread(inbuf, 1, FILE_BUFFER_LENGTH, srcfp);
if (in_len == 0) {break;}
if (EVP_PKEY_encrypt(ctx, outbuf, &out_len, inbuf, in_len) <= 0) {
fprintf(stderr, "Error while encrypting data.\n");
return 0;
}
x = fwrite(outbuf, sizeof(char), in_len, tgtfp);
if (x != in_len) {
fprintf(stderr, "Error while writing to target file.\n");
return 0;
}
}
return 1;
}
int decryptfile(EVP_PKEY *key, FILE *srcfp, FILE *tgtfp) {
ENGINE *e = ENGINE_new();
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key, NULL);
unsigned char *inbuf;
unsigned char *outbuf;
size_t in_len = 0;
size_t out_len = ENC_BUFFER_LENGTH;
int x;
inbuf = (char*)malloc(sizeof(char)*FILE_BUFFER_LENGTH + 1);
outbuf = (char*)malloc(sizeof(char)*ENC_BUFFER_LENGTH + 1);
if (ctx == NULL) {
fprintf(stderr, "Error while creating decryption context.\n");
return 0;
}
if (EVP_PKEY_decrypt_init(ctx) <= 0) {
fprintf(stderr, "Error while initializing decryption context.\n");
return 0;
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) {
fprintf(stderr, "Error while setting decryption padding.\n");
return 0;
}
while (1) {
in_len = fread(inbuf, 1, FILE_BUFFER_LENGTH, srcfp);
if (in_len == 0) { break; }
if (EVP_PKEY_decrypt(ctx, outbuf, &out_len, inbuf, in_len) <= 0) {
fprintf(stderr, "Error while decrypting data.\n");
return 0;
}
x = fwrite(outbuf, sizeof(char), in_len, tgtfp);
if (x != in_len) {
fprintf(stderr, "Error while writing decrypted data to target file.\n");
return 0;
}
}
return 1;
}
I'm a beginner to cryptography (openssl library) and I wanted help on how to send an ECDSA signature through a C socket communication. My plan is:
make socket connection
convert ECDSA SIG object into string
send signature in the form of a string
At the destination, convert the string back to SIG object and verify signing
Here is the code.
static ECDSA_SIG* sig = NULL;
static EC_KEY *eckey = NULL;
int main(int argc, char *argv[])
{
unsigned char* msgDigest = "6df19ccf6b89397c9a9906bfd0848f061352e9b5";
if(ECDSAsign())
printf("signed successfully\n");
else
printf("signing failed\n");
...
}
int ECDSAsign()
{
int ret;
eckey = EC_KEY_new_by_curve_name(NID_secp192k1);
if (eckey == NULL)
{
printf(" error ");
return 0;
}
if (!EC_KEY_generate_key(eckey))
{
printf(" error ");
return 0;
}
unsigned char *buffer, *pp;
int bufLen;
bufLen = ECDSA_size(eckey);
buffer = OPENSSL_malloc(bufLen);
pp = buffer;
unsigned char *dgst = "5df19ccf6b89397c9a9906bfd0848f061352e9ba";
sig = ECDSA_do_sign(dgst, strlen(dgst), eckey);
if (sig == NULL)
{
printf(" Signature NOT generated\n ");
return 0;
}
return 1;
}
How to convert ECDSA SIG signature to array of characters in C
...
The signature is already an array of bytes. That is what is returned from EVP_DigestSignFinal.
In general, you want to use the EVP_* interfaces for signing and verification. Below is from the OpenSSL wiki on EVP Signing and Verifying.
Your job below is to get the ECDSA into the EVP_PKEY*. The function will allocate the signature buffer with OPENSSL_malloc. The caller needs to free it with OPENSSL_free. The buffer is an array of bytes.
int sign_it(const byte* msg, size_t mlen, byte** sig, size_t* slen, EVP_PKEY* pkey)
{
/* Returned to caller */
int result = -1;
if(!msg || !mlen || !sig || !pkey) {
assert(0);
return -1;
}
if(*sig)
OPENSSL_free(*sig);
*sig = NULL;
*slen = 0;
EVP_MD_CTX* ctx = NULL;
do
{
ctx = EVP_MD_CTX_create();
assert(ctx != NULL);
if(ctx == NULL) {
printf("EVP_MD_CTX_create failed, error 0x%lx\n", ERR_get_error());
break; /* failed */
}
const EVP_MD* md = EVP_get_digestbyname("SHA256");
assert(md != NULL);
if(md == NULL) {
printf("EVP_get_digestbyname failed, error 0x%lx\n", ERR_get_error());
break; /* failed */
}
int rc = EVP_DigestInit_ex(ctx, md, NULL);
assert(rc == 1);
if(rc != 1) {
printf("EVP_DigestInit_ex failed, error 0x%lx\n", ERR_get_error());
break; /* failed */
}
rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);
assert(rc == 1);
if(rc != 1) {
printf("EVP_DigestSignInit failed, error 0x%lx\n", ERR_get_error());
break; /* failed */
}
rc = EVP_DigestSignUpdate(ctx, msg, mlen);
assert(rc == 1);
if(rc != 1) {
printf("EVP_DigestSignUpdate failed, error 0x%lx\n", ERR_get_error());
break; /* failed */
}
size_t req = 0;
rc = EVP_DigestSignFinal(ctx, NULL, &req);
assert(rc == 1);
if(rc != 1) {
printf("EVP_DigestSignFinal failed (1), error 0x%lx\n", ERR_get_error());
break; /* failed */
}
assert(req > 0);
if(!(req > 0)) {
printf("EVP_DigestSignFinal failed (2), error 0x%lx\n", ERR_get_error());
break; /* failed */
}
*sig = OPENSSL_malloc(req);
assert(*sig != NULL);
if(*sig == NULL) {
printf("OPENSSL_malloc failed, error 0x%lx\n", ERR_get_error());
break; /* failed */
}
*slen = req;
rc = EVP_DigestSignFinal(ctx, *sig, slen);
assert(rc == 1);
if(rc != 1) {
printf("EVP_DigestSignFinal failed (3), return code %d, error 0x%lx\n", rc, ERR_get_error());
break; /* failed */
}
assert(req == *slen);
if(rc != 1) {
printf("EVP_DigestSignFinal failed, mismatched signature sizes %ld, %ld", req, *slen);
break; /* failed */
}
result = 0;
} while(0);
if(ctx) {
EVP_MD_CTX_destroy(ctx);
ctx = NULL;
}
/* Convert to 0/1 result */
return !!result;
}
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");
...
..
.