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;
}
Related
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;}
I'm trying to setup a client and a server program where the client can download a file from the server.
I have got the client to download a .txt file however, the .txt file is only created, none of the data within it is transferred.
I've figured out the error is either from when the server sends the size of the file to the client OR the client is failing to recognize where the EOF is.
This is my server side code:
int send_all(int clientSocket, const void *buffer, int len) {
const char *pbuf = (const char *) buffer;
while(len > 0) {
int sent = send(clientSocket, pbuf, len, 0);
printf("%d\n", sent);
if(sent < 1) {
printf("%s\n", "Can't write to socket");
return -1;
}
pbuf += sent;
len -= sent;
}
return(0);
}
void SendFileToClient(int clientSocket, char* fileName) {
//int offset;
char buffer[0x1000];
char temp[512] = "/root/Documents/";
const char* filename = fileName;
struct stat s;
strcat(temp, filename);
printf("%s\n", temp);
//append the filenameonto the directory
if(stat(temp, &s) == -1) {
printf("%s\n", "Can't get file info");
return;
}
FILE *file = fopen(temp, "rb");
if(!file) {
printf("%s\n", "Can't open the file for reading");
return;
}
off_t size = s.st_size;
off_t tmp_size = ntohl(size);
if(send_all(clientSocket, &tmp_size, sizeof(tmp_size)) == 0) {
while(size > 0) {
printf("%ld\n", size);
int rval = fread(buffer, 1, min(sizeof(buffer), size), file);
if(rval < 1) {
printf("%s\n", "Cant read from file");
break;
}
if (send_all(clientSocket, buffer, rval) == -1)
break;
size -= rval;
}
}
fclose(file);
}
This is my client side code:
int write_all(FILE *file, const void *buffer, int len) {
const char *pbuf = (const char *) buffer;
while (len > 0) {
int written = fwrite(pbuf, 1, len, file);
if(written < 1) {
printf("%s\n", "Cant write to file");
return -1;
}
pbuf += written;
len -= written;
}
return 0;
}
int read_all(int clientSocket, void *buffer, int len) {
char *pbuf = (char *) buffer;
int total = 0;
while (len > 0) {
int rval = recv(clientSocket, pbuf, len, 0);
if(rval < 0) {
printf("%s\n", "Cant read from socket");
return -1;
}
if(rval == 0) {
printf("%s\n", "Socket disconnected");
return 0;
}
pbuf += rval;
len -= rval;
total += rval;
}
return total;
}
void RecvFile(int clientSocket, const char* filename) {
int rval;
char buffer[0x1000];
//printf("%s\n", filename);
FILE *file = fopen(filename, "wb");
if(!file) {
printf("%s\n", "Cant open file for writing");
return;
}
off_t size = 0;
//this statement is not working
if(read_all(clientSocket, &size, sizeof(size)) == 1) {
size = ntohl(size);
printf("%s%ld\n", "size: ", size);
while(size > 0) {
rval = read_all(clientSocket, buffer, min(sizeof(buffer), size));
printf("%s%d\n", "rval: ", rval);
if(rval < 1)
break;
if(write_all(file, buffer, rval) == -1) {
printf("%s\n", "Cant write to file");
break;
}
}
}
printf("%s\n", "closing file...");
fclose(file);
}
My apologies if the code looks a bit messy - for some reason it didn't paste in with nice formatting.
I believe this is where part of the problem is, but even through various debugging I can't seem to get it to work :(
//this statement is not working
if(read_all(clientSocket, &size, sizeof(size)) == 1) {
Thanks in advance for any help!
I am new in this field, and writing one server and client, but it really confusing that I can't get all the content, but some small clip.
My server code:
read(connfd, name, 20);
//recv(connfd,name,1024,0);
char* a=name;
while(a[0]!='\n'){
a++;
}
a[0]='\0';
printf("name:%s\n", name);
read(connfd, size, 20);
printf("size:%s\n", size);
recv(connfd,buf,8192,0);
printf("buf:%s\n", buf);
if((stream = fopen(name,"w+t"))==NULL){
printf("The file was not opened! \n");
}
int write_length = fwrite(buf,sizeof(char),8192,stream);
bzero(buf,8192);
if(put){
char *res="OK\n";
write(connfd, res, 1024);
}
fclose(stream);
and my client code is:
char buffer[8192];
bzero(buffer,8192);
char * put="PUT\n";
if ((write(fd, put, 8192)) <= 0) {
if (errno != EINTR) {
fprintf(stderr, "Write error: %s\n", strerror(errno));
exit(0);
}
}
struct stat st ;
stat( put_name, &st );
char str[100];
sprintf(str, "%d", st.st_size);
int len;
char *current=NULL;
len=strlen(put_name);
char sendname[1024];
strcpy(sendname,put_name);
strcat(sendname,"\n");
write(fd, sendname, 10);
strcat(str,"\n");
write(fd, str, 10);
FILE *stream;
if((stream = fopen(put_name,"r"))==NULL)
{
printf("The file was not opened! \n");
exit(1);
}
int lengsize = 0;
while((lengsize = fread(buffer,1,8192,stream)) > 0){
if(send(fd,buffer,8192,0)<0){
printf("Send File is Failed\n");
break;
}
bzero(buffer, 8192);
}
Now, I can send all content, but can receive part of them. for example, on my mac, server can receive name but the str is neglected, when I printf the str in the server, it shows the content of file. and the content of file is not the whole file content. Some content disappear. Could you tell me why?
The read and write functions are not guaranteed to send or receive the entire message with a single call. Instead, you're expected to sit in a loop, writing the message incrementally until everything has been sent and reading everything incrementally until everything has been read. For example, if you know exactly how much has been sent, you can do this:
char recvBuffer[BUFFER_SIZE];
int bytesRead = 0;
while (bytesRead < BUFFER_SIZE) {
int readThisTime = read(file, recvBuffer + bytesRead, BUFFER_SIZE - bytesRead);
if (readThisTime == -1) {
// handle error...
}
bytesRead += readThisTime;
}
If you don't know exactly how much has been sent, try this:
char recvBuffer[BUFFER_SIZE];
int bytesRead = 0;
while (bytesRead < BUFFER_SIZE) {
int readThisTime = read(file, recvBuffer + bytesRead, BUFFER_SIZE - bytesRead);
if (readThisTime == -1) {
// handle error...
}
if (readThisTime == 0) break; // Done!
bytesRead += readThisTime;
}
You are ignoring the return values of send() and recv(). You MUST check return values!
When sending the file, lengsize receives how many bytes were actually read from the file. Your client is sending too many bytes when lengsize is < 8192 (typically the last block of the file if the file size is not an even multiple of 8192).
But more importantly, although the client is telling the server the file size, the server is ignoring it to know when to stop reading. The server is also ignoring the return value of recv() to know how many bytes were actually received so it knows how many bytes can safely be written to the output file.
Try something more like this instead:
common:
int readData(int s, void *buf, int buflen)
{
int total = 0;
char *pbuf = (char*) buf;
while (buflen > 0) {
int numread = recv(s, pbuf, buflen, 0);
if (numread <= 0) return numread;
pbuf += numread;
buflen -= numread;
total += numread;
}
return total;
}
int sendData(int s, void *buf, int buflen)
{
int total = 0;
char *pbuf = (char*) buf;
while (buflen > 0) {
int numsent = send(s, pbuf, buflen, 0);
if (numsent <= 0) return numsent;
pbuf += numsent;
buflen -= numsent;
total += numsent;
}
return total;
}
int readInt32(int s, int32_t *value)
{
int res = readData(s, value, sizeof(*value));
if (res > 0) *value = ntohl(*value);
return res;
}
int sendInt32(int s, int32_t value)
{
value = htonl(value);
return sendData(s, &value, sizeof(value));
}
char* readStr(int s)
{
int32_t size;
if (readInt32(s, &size) <= 0)
return NULL;
char *str = malloc(size+1);
if (!str)
return NULL;
if (readData(s, str, size) <= 0) {
free(str);
return NULL;
}
str[size] = '\0';
return str;
}
int sendStr(int s, const char *str)
{
int len = strlen(str);
int res = sendInt32(s, len);
if (res > 0)
res = sendData(s, str, len);
return res;
}
server:
char buffer[8192];
char *name = readStr(connfd);
if (!name) {
// error handling ...
sendStr(connfd, "Socket read error");
return;
}
printf("name:%s\n", name);
int32_t filesize;
if (readInt32(connfd, &filesize) <= 0) {
// error handling ...
free(name);
sendStr(connfd, "Socket read error");
return;
}
printf("size:%d\n", filesize);
if ((stream = fopen(name, "wb")) == NULL) {
// error handling ...
printf("The file was not opened!\n");
free(name);
sendStr(connfd, "File not opened");
return;
}
while (filesize > 0) {
int numread = readData(connfd, buf, min(filesize, sizeof(buffer)));
if (numread <= 0) {
// error handling ...
close(stream);
free(name);
sendStr(connfd, "Socket read error");
return;
}
printf("buf:%.*s\n", numread, buf);
if (fwrite(buf, 1, numread, stream) != numread) {
// error handling ...
close(stream);
free(name);
sendStr(connfd, "File write error");
return;
}
filesize -= numread;
}
fclose(stream);
free(name);
sendStr(connfd, "OK");
client:
char buffer[8192];
struct stat st;
if (stat( put_name, &st ) != 0) {
// error handling ...
exit(0);
}
if ((stream = fopen(put_name, "rb")) == NULL) {
// error handling ...
printf("The file was not opened!\n");
exit(0);
}
if (sendStr(fd, put_name) <= 0) {
// error handling ...
close(stream);
exit(0);
}
int32_t filesize = st.st_size;
if (sendInt32(fd, filesize) <= 0) {
// error handling ...
close(stream);
exit(0);
}
int lengsize;
while (filesize > 0) {
lengsize = fread(buffer, 1, min(filesize , sizeof(buffer)), stream);
if (lengsize <= 0) {
printf("Read File Failed\n");
// error handling ...
close(stream);
exit(0);
}
if (sendData(fd, buffer, lengsize) <= 0) {
printf("Send File Failed\n");
// error handling ...
close(stream);
exit(0);
}
filesize -= lengsize;
}
close(stream);
char *resp = readStr(fd);
if (!resp) {
// error handling ...
exit(0);
}
if (strcmp(resp, "OK") == 0)
printf("Send File OK\n");
else
printf("Send File Failed: %s\n", resp);
free(resp);
I'm writing a C program that encrypts(based on the private key) and decrypts(based on the public key) text. I'm trying to do this with the OpenSSL lib. Does anyone know any good tutorial, quick starting guide or sample code? I haven't found any decent one on the web.
Here's an example I created for encrypting a file using RSA for the asymmetric algorithm and AES-128-CBC for the symmetric algorithm, with the OpenSSL EVP functions:
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <arpa/inet.h> /* For htonl() */
int do_evp_seal(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 = NULL;
int eklen;
uint32_t eklen_n;
unsigned char iv[EVP_MAX_IV_LENGTH];
if (!PEM_read_RSA_PUBKEY(rsa_pkey_file, &rsa_pkey, NULL, NULL))
{
fprintf(stderr, "Error loading RSA Public Key File.\n");
ERR_print_errors_fp(stderr);
retval = 2;
goto out;
}
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_SealInit(&ctx, EVP_aes_128_cbc(), &ek, &eklen, iv, &pkey, 1))
{
fprintf(stderr, "EVP_SealInit: failed.\n");
retval = 3;
goto out_free;
}
/* First we write out the encrypted key length, then the encrypted key,
* then the iv (the IV length is fixed by the cipher we have chosen).
*/
eklen_n = htonl(eklen);
if (fwrite(&eklen_n, sizeof eklen_n, 1, out_file) != 1)
{
perror("output file");
retval = 5;
goto out_free;
}
if (fwrite(ek, eklen, 1, out_file) != 1)
{
perror("output file");
retval = 5;
goto out_free;
}
if (fwrite(iv, EVP_CIPHER_iv_length(EVP_aes_128_cbc()), 1, out_file) != 1)
{
perror("output file");
retval = 5;
goto out_free;
}
/* Now we process the input file and write the encrypted data to the
* output file. */
while ((len = fread(buffer, 1, sizeof buffer, in_file)) > 0)
{
if (!EVP_SealUpdate(&ctx, buffer_out, &len_out, buffer, len))
{
fprintf(stderr, "EVP_SealUpdate: 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_SealFinal(&ctx, buffer_out, &len_out))
{
fprintf(stderr, "EVP_SealFinal: 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;
}
int main(int argc, char *argv[])
{
FILE *rsa_pkey_file;
int rv;
if (argc < 2)
{
fprintf(stderr, "Usage: %s <PEM RSA Public Key File>\n", argv[0]);
exit(1);
}
rsa_pkey_file = fopen(argv[1], "rb");
if (!rsa_pkey_file)
{
perror(argv[1]);
fprintf(stderr, "Error loading PEM RSA Public Key File.\n");
exit(2);
}
rv = do_evp_seal(rsa_pkey_file, stdin, stdout);
fclose(rsa_pkey_file);
return rv;
}
And the corresponding decryption example:
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <arpa/inet.h> /* For htonl() */
int do_evp_unseal(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;
unsigned int eklen;
uint32_t eklen_n;
unsigned char iv[EVP_MAX_IV_LENGTH];
if (!PEM_read_RSAPrivateKey(rsa_pkey_file, &rsa_pkey, NULL, NULL))
{
fprintf(stderr, "Error loading RSA Private Key File.\n");
ERR_print_errors_fp(stderr);
retval = 2;
goto out;
}
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));
/* First need to fetch the encrypted key length, encrypted key and IV */
if (fread(&eklen_n, sizeof eklen_n, 1, in_file) != 1)
{
perror("input file");
retval = 4;
goto out_free;
}
eklen = ntohl(eklen_n);
if (eklen > EVP_PKEY_size(pkey))
{
fprintf(stderr, "Bad encrypted key length (%u > %d)\n", eklen,
EVP_PKEY_size(pkey));
retval = 4;
goto out_free;
}
if (fread(ek, eklen, 1, in_file) != 1)
{
perror("input file");
retval = 4;
goto out_free;
}
if (fread(iv, EVP_CIPHER_iv_length(EVP_aes_128_cbc()), 1, in_file) != 1)
{
perror("input file");
retval = 4;
goto out_free;
}
if (!EVP_OpenInit(&ctx, EVP_aes_128_cbc(), ek, eklen, iv, pkey))
{
fprintf(stderr, "EVP_OpenInit: failed.\n");
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_SealFinal: 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;
}
int main(int argc, char *argv[])
{
FILE *rsa_pkey_file;
int rv;
if (argc < 2)
{
fprintf(stderr, "Usage: %s <PEM RSA Private Key File>\n", argv[0]);
exit(1);
}
rsa_pkey_file = fopen(argv[1], "rb");
if (!rsa_pkey_file)
{
perror(argv[1]);
fprintf(stderr, "Error loading PEM RSA Private Key File.\n");
exit(2);
}
rv = do_evp_unseal(rsa_pkey_file, stdin, stdout);
fclose(rsa_pkey_file);
return rv;
}
I think that's fairly easy to follow. As written both commands can be used as part of a pipeline (they take input on stdin and write output to stdout).
I am trying to implement OpenSSL RSA, following 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);
BIO *bio_out = NULL;
FILE *fp;
if (!ctx)
{
perror("Error in CTX \n");
}
if (EVP_PKEY_keygen_init(ctx) <= 0)
{
perror("Error in EVP_PKEY_keygen_init \n");
}
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) <= 0)
{
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");
if ((bio_out = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL)
{
perror("Error in new BIO \n");
}
BIO_set_flags(bio_out, BIO_FLAGS_WRITE);
EVP_PKEY_print_private(bio_out,pkey,5, NULL);
fclose(fp);
BIO_free(bio_out);
fp = fopen(PUBFILE, "w");
if ((bio_out = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL)
{
perror("Error in new BIO \n");
}
BIO_set_flags(bio_out, BIO_FLAGS_WRITE);
EVP_PKEY_print_public(bio_out,pkey,5, NULL);
fclose(fp);
BIO_free(bio_out);
return 0;
}
And here is the pub.pem:
Public-Key: (2048 bit)
Modulus:
00:be:bc:04:74:39:5b:31:2d:76:bf:41:9a:3e:6e:
d0:cf:8f:c3:25:86:6b:38:ff:58:96:fc:bb:2b:d9:
af:df:fa:bf:aa:1e:c7:58:1d:2b:ce:6c:90:0f:d3:
30:b7:90:fc:1c:7c:84:f3:31:80:3e:04:ad:bd:74:
dc:92:60:01:9e:40:82:31:b3:8d:a5:ac:fb:81:83:
c5:e9:e6:56:0b:f4:df:dd:f3:e4:7f:3b:b6:85:d7:
c9:41:08:3f:bf:15:bd:6d:19:ff:13:fc:4a:52:59:
93:77:b0:be:78:01:da:db:b2:7d:f9:38:98:e0:d2:
45:79:3c:60:0d:a1:65:74:d2:9c:9b:7c:15:2c:e4:
ee:eb:76:52:f3:61:95:8b:d6:fb:5e:fb:f7:c6:40:
70:77:86:fb:60:87:07:f3:58:49:74:d6:0a:cf:d4:
70:09:50:73:20:1e:4e:52:4c:59:34:a0:f5:7a:80:
57:e5:8a:8c:d1:a1:94:a4:ab:75:99:fd:55:62:06:
34:dd:83:00:aa:3f:c8:0a:a1:0e:80:1a:ba:42:6e:
23:37:eb:bd:1f:e4:a2:c4:fa:2a:84:84:e4:dc:ec:
a2:a5:36:2c:c6:cb:28:81:8c:89:15:64:f4:ef:ec:
1e:c8:c3:d5:c2:02:54:ce:bb:72:20:b4:4a:05:2f:
dc:75
Exponent: 65537 (0x10001)
Following is the code which I am using for encryption:
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <arpa/inet.h> /* For htonl() */
int do_evp_seal(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];
if (!PEM_read_RSA_PUBKEY(rsa_pkey_file, &rsa_pkey, NULL, NULL))
{
fprintf(stderr, "Error loading RSA Public Key File.\n");
// here I am getting this error:
// 140121481717416:error:0906D06C:lib(9):func(109):reason(108):pem_lib.c:696:Expecting: PUBLIC KEY
ERR_print_errors_fp(stderr);
retval = 2;
goto out;
}
EVP_CIPHER_CTX_init(&ctx);
ek = malloc(EVP_PKEY_size(pkey));
if (!EVP_SealInit(&ctx, EVP_aes_128_cbc(), &ek, &eklen, iv, &pkey, 1))
{
fprintf(stderr, "EVP_SealInit: failed.\n");
retval = 3;
goto out_free;
}
/* First we write out the encrypted key length, then the encrypted key,
* then the iv (the IV length is fixed by the cipher we have chosen).
*/
eklen_n = htonl(eklen);
if (fwrite(&eklen_n, sizeof eklen_n, 1, out_file) != 1)
{
perror("output file");
retval = 5;
goto out_free;
}
if (fwrite(ek, eklen, 1, out_file) != 1)
{
perror("output file");
retval = 5;
goto out_free;
}
if (fwrite(iv, EVP_CIPHER_iv_length(EVP_aes_128_cbc()), 1, out_file) != 1)
{
perror("output file");
retval = 5;
goto out_free;
}
/* Now we process the input file and write the encrypted data to the
* output file. */
while ((len = fread(buffer, 1, sizeof buffer, in_file)) > 0)
{
if (!EVP_SealUpdate(&ctx, buffer_out, &len_out, buffer, len))
{
fprintf(stderr, "EVP_SealUpdate: 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_SealFinal(&ctx, buffer_out, &len_out))
{
fprintf(stderr, "EVP_SealFinal: 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;
}
int main(int argc, char *argv[])
{
FILE *rsa_pkey_file;
int rv;
rsa_pkey_file = fopen("pub.pem", "rb");
if (!rsa_pkey_file)
{
fprintf(stderr, "Error loading PEM RSA Public Key File.\n");
exit(2);
}
rv = do_evp_seal(rsa_pkey_file, stdin, stdout);
fclose(rsa_pkey_file);
return rv;
}
Why is PEM_read_RSA_PUBKEY returning ERROR: Expecting PUBLIC KEY?
OK, I found out myself I have write PEM_write_bio_PUBKEY(bio_out,pkey ); instead of EVP_PKEY_print_public(bio_out,pkey)