EVP reading PEM private key not working properly - c

When I run the following code, which generates a key, writes it to a string, prints it, reads it into the key, and prints it again, against OpenSSL_1_0_2e:
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#define RSA_KEYLEN 2048
int main()
{
// Key generation
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
EVP_PKEY* key = NULL;
EVP_PKEY_keygen_init(ctx);
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, RSA_KEYLEN);
EVP_PKEY_keygen(ctx, &key);
EVP_PKEY_CTX_free(ctx);
// Serialize to string
unsigned char* keyStr;
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, 0, NULL);
int priKeyLen = BIO_pending(bio);
keyStr = (unsigned char*)malloc(priKeyLen + 1);
BIO_read(bio, keyStr, priKeyLen);
keyStr[priKeyLen] = '\0';
BIO_free_all(bio);
// Print the string
printf("%s", keyStr);
// Reset the key
EVP_PKEY_free(key);
key = NULL;
// Read from string
bio = BIO_new(BIO_s_mem());
BIO_write(bio, keyStr, priKeyLen);
PEM_read_bio_PrivateKey(bio, &key, NULL, NULL);
BIO_free_all(bio);
// Free the string
free(keyStr);
// Serialize to string (again)
bio = BIO_new(BIO_s_mem());
PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, 0, NULL);
priKeyLen = BIO_pending(bio);
keyStr = (unsigned char*)malloc(priKeyLen + 1);
BIO_read(bio, keyStr, priKeyLen);
keyStr[priKeyLen] = '\0';
BIO_free_all(bio);
// Print string
printf("%s", keyStr);
}
The private key is obviously way too short in the second output. What am I doing wrong?

The solution to my particular problem was I was trying to set the Public and Private key on the EVP_PKEY thinking that I needed to load both to use it as a keypair. Actually, you only load one of the two. With the private key, the public key is derived.

Related

segfault with EVP_PKEY_decrypt()

I'm getting a segfault when running this code and idk why. Any ideas?:
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <string.h>
#include <openssl/err.h>
int main (void)
{
char privateKey[] = "-----BEGIN RSA PRIVATE KEY-----\n"\
"MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu\n"\
"KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm\n"\
"o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k\n"\
"TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7\n"\
"9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy\n"\
"v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs\n"\
"/5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00\n"\
"-----END RSA PRIVATE KEY-----\n";
char ciphertext[] = "h3j3zLT2jXCaZuwF7cgUE/Zmc/5IsIfKbaTiBhpCJo86AiyuoA3Yvni+Lrm5wu2OGv2h5R7Zu3voFcHugiystw==";
// base64 decode ciphertext
EVP_DecodeBlock(ciphertext, ciphertext, strlen(ciphertext));
BIO* bo = BIO_new(BIO_s_mem());
BIO_write(bo, privateKey, strlen(privateKey));
EVP_PKEY* pkey = 0;
PEM_read_bio_PrivateKey(bo, &pkey, 0, 0);
BIO_free(bo);
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
EVP_PKEY_decrypt_init(ctx);
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");
size_t plaintext_len;
unsigned char* plaintext;
EVP_PKEY_decrypt(ctx, plaintext, &plaintext_len, ciphertext, strlen(ciphertext));
//plaintext[plaintext_len] = '\0';
printf("%s\n", plaintext);
}
You forgot to initialize plaintext before passing that to EVP_PKEY_decrypt.
Refering evp_pkey_decrypt(3) - Linux man page, the part
unsigned char* plaintext;
EVP_PKEY_decrypt(ctx, plaintext, &plaintext_len, ciphertext, strlen(ciphertext));
//plaintext[plaintext_len] = '\0';
should be
unsigned char* plaintext;
/* determine output size */
EVP_PKEY_decrypt(ctx, NULL, &plaintext_len, ciphertext, strlen(ciphertext));
/* allocate memory (+1 for terminating null-character) */
plaintext = OPENSSL_malloc(plaintext_len + 1);
if (plaintext == NULL) {
/* allocation failed */
return 1;
} else {
EVP_PKEY_decrypt(ctx, plaintext, &plaintext_len, ciphertext, strlen(ciphertext));
plaintext[plaintext_len] = '\0'; /* required for %s */
}

Load public key to create rsa object for public encryption

I am trying to load a rsa object from a generated public key. I used PEM_write_bio_RSAPublicKey to generate the public key string. Then I used PEM_read_bio_RSA_PUBKEY to load the rsa object from the public key string. The problem is the rsa object is null. The generated string looks okay as far as I can tell. Any ideas?
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAxIReUspesPy6a4CPBjt/4Jt+H13q9MekMiutzNKdNO1uuwqcdqDX
pKPeTKXyUH6oCyRdUxkk6IVXGlBlxtW7OsxaYWhpfl9z3CCERCEpFmzN++dvlK2v
mckFL66e9q6Y+HwgyP1LJqrszeqlg2d29TCVKfD/UURVNmc/nPPjs9nO+IDhh7+P
NTQ2OqGBq8ghwVL5ZZyW3yVO5OAbRB6pjKBe9+j4B2TGnD5JO9Nu0jlFANZOKFJu
HDVE3XuTvOkuzL2i8Lwp4Myk42tbIgcCe4G58vKFddL651rWhg4hN3fRSx5YtDnQ
r5cgfNBOAww58S8lwXgU8lvzvEoNV+WMgwIDAQAB
-----END RSA PUBLIC KEY-----
gcc test_public_private_key.c -lcrypto -o test
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
char* get_public_key() {
RSA* rsa = RSA_new();
int kbits = 2048;
BIGNUM* bne = BN_new();
BN_set_word(bne, RSA_F4);
int status = RSA_generate_key_ex(rsa, kbits, bne, NULL);
if(status == 0) {
fprintf(stderr, "rsa key generation failed\n");
}
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPublicKey(bio, rsa);
int length = BIO_pending(bio);
char* pem_key = malloc(length+1);
memset(pem_key, '\0', length+1);
BIO_read(bio, pem_key, length);
return pem_key;
}
int main(int argc, char* argv[]) {
char* public_key = get_public_key();
printf("%s", public_key);
BIO* keybio = BIO_new_mem_buf(public_key, -1);
if (keybio == NULL) {
fprintf(stderr, "failed to create key BIO");
}
printf("keybio: %p\n", keybio);
RSA* rsa = PEM_read_bio_RSA_PUBKEY(keybio, NULL, NULL, NULL);
printf("rsa result %p\n", rsa);
BIO_free(keybio);
free(public_key);
return 0;
}
I thought it would be good to add that the reason this didn't work is because PEM_read_RSA_PUBKEY expects a SubjectPublicKeyInfo structure, which starts with BEGIN PUBLIC KEY, while PEM_read_RSAPublicKey expects a RSAPublicKey structure, which starts with BEGIN RSA PUBLIC KEY.
PEM_write_bio_RSAPublicKey generated the latter which corresponds to the second function, PEM_read_RSAPublicKey.

Decrypting an AES-encrypted message with an RSA-encrypted key with EVP tool

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.

DSA signature verification in C do not match with signature verification in console. OpenSSL

I wrote a code to sign a message that apparently works fine, it can sign a message with a DSA private key (512 bits), and verify it later with the respective public key (no problems here). I´m using Windows 7.
But,
If I want to verify the sign in the console, openssl command always returns: Verification Failure.
#include <stdio.h>
#include <string.h>
#include <openssl\dsa.h>
#include <openssl\engine.h>
#include <openssl\pem.h>
int main(){
char privkey[] = {
"-----BEGIN DSA PRIVATE KEY-----\n"
"MIH4AgEAAkEA3F41fxvcwGZeFxXg2v0/5SR+cxTizT25QugwZgrC7u2zQYTO1Qu0\n"
"PPDKUrLxkaLzKsUEJbQ1DImnG/FxtRjH7QIVAOp+o1qPhOI4DtnvYS86ynTxhDcF\n"
"AkAyKbiUxJigARuuVVlGn4emXOtrT+Al+gmKbbVFfkS62RhSZexQ9+mBLv0/1R8T\n"
"k37AwuybnflijiPjLxB1ZL00AkA2DXjw+0PJOyrQfn2Q44uHyZMG2WfXqT7CIz26\n"
"ZiIAHDOkZQhOvPLqCKAXfwHgGrgl2JLovhVY8nPMdNk2vJijAhUAzK9TuVobhOE8\n"
"kU0xl8lqaI/PMyg=\n"
"-----END DSA PRIVATE KEY-----"
};
char pubkey[] = {
"-----BEGIN PUBLIC KEY-----\n"
"MIHwMIGoBgcqhkjOOAQBMIGcAkEA3F41fxvcwGZeFxXg2v0/5SR+cxTizT25Qugw\n"
"ZgrC7u2zQYTO1Qu0PPDKUrLxkaLzKsUEJbQ1DImnG/FxtRjH7QIVAOp+o1qPhOI4\n"
"DtnvYS86ynTxhDcFAkAyKbiUxJigARuuVVlGn4emXOtrT+Al+gmKbbVFfkS62RhS\n"
"ZexQ9+mBLv0/1R8Tk37AwuybnflijiPjLxB1ZL00A0MAAkA2DXjw+0PJOyrQfn2Q\n"
"44uHyZMG2WfXqT7CIz26ZiIAHDOkZQhOvPLqCKAXfwHgGrgl2JLovhVY8nPMdNk2\n"
"vJij\n"
"-----END PUBLIC KEY-----"
};
char message[] = {"Hello World!"};
//save message for console test
FILE *message_file = fopen ("message.txt","w");
fwrite(message, strlen(message), 1, message_file);
fclose(message_file);
//save private key for console test
FILE *privkey_file = fopen ("privkey.pem","w");
fwrite(privkey, strlen(privkey), 1, privkey_file);
fclose(privkey_file);
//save public key for console test
FILE *pubkey_file = fopen ("pubkey.pem","w");
fwrite(pubkey, strlen(pubkey), 1, pubkey_file);
fclose(pubkey_file);
//Prepare sign -----------------------------------------------------
BIO *priv_bio;
priv_bio = BIO_new_mem_buf(privkey, -1);
if(priv_bio == NULL){ERR_print_errors_fp(stdout);return 1;}
DSA *private_key;
private_key = PEM_read_bio_DSAPrivateKey(priv_bio, NULL, NULL, NULL);
if(private_key == NULL){ERR_print_errors_fp(stdout);return 2;}
unsigned int result, sign_length;
unsigned char signature[1000] = {0};
//generate sign
result = DSA_sign(NULL, (const unsigned char*)message, strlen(message),
(unsigned char*)signature, &sign_length, private_key);
if(result!=1){ERR_print_errors_fp(stdout);return 3;}
//save signature for console test
FILE *sign_file = fopen ("signature","wb");
fwrite((void*)signature, sign_length, 1, sign_file);
fclose(sign_file);
//verify sign -----------------------------------------------------
BIO *pub_bio;
pub_bio = BIO_new_mem_buf(pubkey, -1);
if(pub_bio == NULL){ERR_print_errors_fp(stdout);return 4;}
DSA *public_key;
public_key = PEM_read_bio_DSA_PUBKEY(pub_bio, NULL, NULL, NULL);
if(public_key == NULL){ERR_print_errors_fp(stdout);return 5;}
result = DSA_verify(NULL, (const unsigned char*)message, strlen(message),
signature, sign_length, public_key);
if(result>0){
printf("Verification OK\n");
}else{
printf("Verification Failure. Error = %i\n",result);
}
BIO_free_all(priv_bio);
DSA_free(private_key);
DSA_free(public_key);
//Console verification
system("openssl dgst -verify pubkey.pem -signature signature message.txt");
return 0;
}
Output:
Verification OK
Verification Failure
Solved:
Apply SHA1 to the digest (message) before DSA_sign, and
apply SHA1 to the digest (message) before DSA_verify.
This makes compatible the signature to work in console with OpenSSL.
To use DSA with openssl you must pass in the -dss1 to the dgst, otherwise it will use SHA1. Try this:
system("openssl dgst -dss1 -verify pubkey.pem -signature signature message.txt");

Issues with encrypting a file using openssl evp api(aes256cbc)

I am trying to read a file(.txt) in this case and encrypting/decrypting it with AES256CBC using EVP api of openssl.(read(plain.txt)->create(encrypt.txt)->create(decrypt.txt))
# include <stdio.h>
# include <stdlib.h>
# include <openssl/evp.h>
# include <openssl/aes.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <string.h>
# include <assert.h>
# include <error.h>
# include "debug.h"
# define SIZE 32
char buf[SIZE];
int aes_init(unsigned char* pwd, unsigned int pwd_len, unsigned char * salt, EVP_CIPHER_CTX *e_ctx, EVP_CIPHER_CTX *d_ctx)
{
int i, rounds =5; /* rounds */
unsigned char key[32], iv[32];
i = EVP_BytesToKey(EVP_aes_256_cbc(),EVP_sha1(),salt,pwd,pwd_len,rounds,key,iv);
if(i != 32)
{
printf("\n Error,Incorrect key size generated:%d:\n",i);
return -1;
}
EVP_CIPHER_CTX_init(e_ctx);
EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
EVP_CIPHER_CTX_init(d_ctx);
EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
return 0;
}
unsigned char* aes_encrypt(EVP_CIPHER_CTX *e,unsigned char * plain_text, unsigned int * len ) /* this function encryptes the file:fd is passed as parameter */
{
int ci_len = (*len) + AES_BLOCK_SIZE;
int flen =0;
unsigned char * cipher_text = malloc(ci_len);
EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); /* allows reusing of e for multiple cipher cycles */
EVP_EncryptUpdate(e, cipher_text, &ci_len, plain_text, *len); /* Update cipher text */
EVP_EncryptFinal_ex(e, cipher_text+ci_len, &flen); /* updates the remaining bytes */
*len = ci_len + flen;
return cipher_text;
}
unsigned char* aes_decrypt(EVP_CIPHER_CTX *e, unsigned char * c_text, unsigned int * len)
{
int pi_len = (*len);
int flen = 0;
unsigned char * plain_text = malloc(pi_len);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plain_text, &pi_len, c_text, *len);
EVP_DecryptFinal_ex(e, plain_text+pi_len, &flen);
(*len) = pi_len + flen;
return plain_text;
}
int main(int argc,char **argv)
{
if(argc != 2)
{
perror("\n Error:\nCorrect Usage: Enter Password to be used");
exit(-1);
}
EVP_CIPHER_CTX en,de; /* The EVP structure which keeps track of all crypt operations see evp.h for details */
int in, out, fd, dec,i =0; /* fd for input and output files and random dev*/
unsigned int pwd_len = strlen((const char *)argv[1]); /* Length of the pwd supplied by the user */
unsigned char *pwd =(unsigned char*) argv[1]; /* Pointer to the pwd supplied by the user */
unsigned int rd= 0;
unsigned char salt[8];
unsigned char * encry = NULL, *decry = NULL;
i =0;
if((in = open("plain.txt",O_RDONLY)) == -1) /* Opening a plain text file for encryption */
{
perror("\n Error,Opening file for reading::");
exit(-1);
}
if((fd = open("/dev/random", O_RDONLY)) == -1)
{
perror("\n Error,Opening /dev/random::");
exit(-1);
}
else
{
if(read(fd,salt,8) == -1)
{
perror("\n Error,reading from /dev/random::");
exit(-1);
}
}
if(aes_init(pwd,pwd_len,(unsigned char*) salt,&en,&de)) /* Generating Key and IV and initializing the EVP struct */
{
perror("\n Error, Cant initialize key and IV:");
return -1;
}
if((out = open("encrypt.txt",O_RDWR|O_CREAT,0400 | 0200)) == -1)
{
dbug_p("ENC%d",out);
perror("\n Error,Opening the file to be written::");
exit(-1);
}
rd =0;
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::%d*\n",buf,rd);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
if((write(out,encry,rd)) != rd)
{
perror("\n Error,Required encrypted bytes not written::");
exit(-1);
}
free(encry);
}
rd =0;
if((dec = open("dec22.txt",O_RDWR|O_CREAT,0400 | 0200)) == -1)
{
dbug_p("dec%d",dec);
perror("\n Error,Opening the decrypting o/p file::");
exit(-1);
}
if((lseek(out,0,SEEK_SET)) != 0) perror("\n Error:setting lseek::");
for(i=0;i<SIZE;i++) buf[i] =0;
while((rd = read(out,dbuf,SIZE)) >0)
{
decry = aes_decrypt(&de,(unsigned char*) dbuf, &rd);
if((write(dec,decry,rd)) != rd)
{
perror("\n Error,Required decrypted bytes not written::");
exit(-1);
}
free(decry);
}
close(in);
close(fd);
EVP_CIPHER_CTX_cleanup(&en);
EVP_CIPHER_CTX_cleanup(&de);
return 0;
}
My problem was that my when i decrypt an encrypted file i get a file which is not properly decrypted (e.g. correct stringgarbagecorrect stringgarbage ...)
abhi#ubuntu:~/mpro/EFF$ cat plain.txt
Today is tuesday
tomorrow is wednesday
then thursday and friday and saturday
finally sunday
Decrypted file
cat dec22.txt
Today is tuesdayw)Q������O-%�A�8���R��.�O���and saturday
finally sunday
What can be the reason for this. Is it reading something else also or i am making any foolish error somewhere.
EDIT: If I just encrypt an array (tried with 36char long) it correctly encrypted and decrypted without printing any garbage.
I guess i am missing(not handling) some *nix file structure details ..??
Or is there any better way to do this encryption on a file.?
Many thanks
I think your analysis is wrong. This loop is problematic:
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::\n",buf);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
dbug_p("\n EN::%s::\n",encry);
decry = aes_decrypt(&de,(unsigned char*) encry,&rd);
dbug_p("\n DE::%s::\n",decry);
free(encry);
free(decry);
}
Firstly because you print using %s which expects a zero terminator. However, the encrypted/decrypted data is not zero terminated. Instead, you should print rd characters using a loop like for (i = 0; i < rd; i++) printf("%02x "); - this is why your analysis of the problem is likely flawed.
Secondly, I assume that in your real problem, you are reading SIZE bytes at a time and sending them to aes_decrypt() separately. This will fail because EVP_DecryptFinal_ex() is getting called too early (before all the encrypted blocks were read). You have two options. Either you send the read bytes through EVP_DecryptUpdate() in each loop iteration, and call EVP_DecryptFinal() after completing the loop (and init accordingly before the loop), or you read the whole file into a buffer first, and then send it through aes_decrypt() in one go.
Or in other words, you need to send the whole data block resulting from aes_encrypt() later to aes_decrypt(). You cannot send them in different chunks, unless you split the functions up and use the EVP "update" functions on the separate chunks.
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::%d*\n",buf,rd);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
and,
unsigned char* aes_encrypt(EVP_CIPHER_CTX *e,unsigned char * plain_text, unsigned int * len ) /* this function encryptes the file:fd is passed as parameter */
{
int ci_len = (*len) + AES_BLOCK_SIZE;
int flen =0;
unsigned char * cipher_text = malloc(ci_len);
EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); /* allows reusing of e for multiple cipher cycles */
EVP_EncryptUpdate(e, cipher_text, &ci_len, plain_text, *len); /* Update cipher text */
EVP_EncryptFinal_ex(e, cipher_text+ci_len, &flen); /* updates the remaining bytes */
You are calling EVP_EncryptFinal_ex multiple times. It is supposed to be called only once at the end.
Same is true for how you are doing decryption.
Here is a simple example from the man page on how to do encrypt.
Have a similar function for decryption and it should work.
int do_crypt(char *outfile)
{
unsigned char outbuf[1024];
int outlen, tmplen;
/* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
unsigned char iv[] = {1,2,3,4,5,6,7,8};
char intext[] = "Some Crypto Text";
EVP_CIPHER_CTX ctx;
FILE *out;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, key, iv);
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, strlen(intext)))
{
/* Error */
return 0;
}
/* Buffer passed to EVP_EncryptFinal() must be after data just
* encrypted to avoid overwriting it.
*/
if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
{
/* Error */
return 0;
}
outlen += tmplen;
EVP_CIPHER_CTX_cleanup(&ctx);
/* Need binary mode for fopen because encrypted data is
* binary data. Also cannot use strlen() on it because
* it wont be null terminated and may contain embedded
* nulls.
*/
out = fopen(outfile, "wb");
fwrite(outbuf, 1, outlen, out);
fclose(out);
return 1;
}
the following example is reading a file as your case. See how Update (called multiple times) and Final (once at the end) routines are used.
int do_crypt(FILE *in, FILE *out, int do_encrypt)
{
/* Allow enough space in output buffer for additional block */
inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
int inlen, outlen;
/* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = "0123456789";
unsigned char iv[] = "12345678";
/* Don't set key or IV because we will modify the parameters */
EVP_CIPHER_CTX_init(&ctx);
EVP_CipherInit_ex(&ctx, EVP_rc2(), NULL, NULL, NULL, do_encrypt);
EVP_CIPHER_CTX_set_key_length(&ctx, 10);
/* We finished modifying parameters so now we can set key and IV */
EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, do_encrypt);
for(;;)
{
inlen = fread(inbuf, 1, 1024, in);
if(inlen <= 0) break;
if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))
{
/* Error */
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))
{
/* Error */
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}

Resources