I’ve been using OpenSSL a LOT for work lately and I’ve learned some interesting stuff. Here’s a quick guide on how to encrypt and decrypt files using AES in CBC or CTR mode using 256 bit keys and 128 bits IVs. To do this, I used the EVP API in OpenSSL, which allows you to easily encrypt a file using any cipher of your liking.
I am assuming some crypto knowledge here, such as block ciphers, modes of operation, keys, IVs, plaintexts, ciphertexts, etc. There’s a great course on Coursera.org on cryptography if you want to learn more about these things.
So, let’s get to it! :) Encrypting or decrypting a file using the EVP API in OpenSSL can be done as follows in C:
int aes_encrypt_file(const char * infile, const char * outfile, const void * key, const void * iv, const EVP_CIPHER * cipher, int enc)
{
assert(cipher != NULL);
int rc = -1;
int cipher_block_size = EVP_CIPHER_block_size(cipher);
assert(cipher_block_size <= BUF_SIZE);
// The output buffer size needs to be bigger to accomodate incomplete blocks
// See EVP_EncryptUpdate documentation for explanation:
// http://lmgtfy.com/?q=EVP_EncryptUpdate
int insize = BUF_SIZE;
int outsize = insize + (cipher_block_size - 1);
unsigned char inbuf[insize], outbuf[outsize];
int ofh = -1, ifh = -1;
int u_len = 0, f_len = 0;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
// Open the input and output files
rc = AES_ERR_FILE_OPEN;
if((ifh = open(infile, O_RDONLY)) == -1) {
fprintf(stderr, "ERROR: Could not open input file %s, errno = %s\n", infile, strerror(errno));
goto cleanup;
}
if((ofh = open(outfile, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) {
fprintf(stderr, "ERROR: Could not open output file %s, errno = %s\n", outfile, strerror(errno));
goto cleanup;
}
// Initialize the AES cipher for enc/dec
rc = AES_ERR_CIPHER_INIT;
if(EVP_CipherInit_ex(&ctx, cipher, NULL, key, iv, enc) == 0) {
fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto cleanup;
}
// Read, pass through the cipher, write.
int read_size, len;
while((read_size = read(ifh, inbuf, BUF_SIZE)) > 0)
{
dbg("Read %d bytes, passing through CipherUpdate...\n", read_size);
if(EVP_CipherUpdate(&ctx, outbuf, &len, inbuf, read_size) == 0) {
rc = AES_ERR_CIPHER_UPDATE;
fprintf(stderr, "ERROR: EVP_CipherUpdate failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto cleanup;
}
dbg("\tGot back %d bytes from CipherUpdate...\n", len);
dbg("Writing %d bytes to %s...\n", len, outfile);
if(write(ofh, outbuf, len) != len) {
rc = AES_ERR_IO;
fprintf(stderr, "ERROR: Writing to the file %s failed. errno = %s\n", outfile, strerror(errno));
goto cleanup;
}
dbg("\tWrote %d bytes\n", len);
u_len += len;
}
// Check last read succeeded
if(read_size == -1) {
rc = AES_ERR_IO;
fprintf(stderr, "ERROR: Reading from the file %s failed. errno = %s\n", infile, strerror(errno));
goto cleanup;
}
// Finalize encryption/decryption
rc = AES_ERR_CIPHER_FINAL;
if(EVP_CipherFinal_ex(&ctx, outbuf, &f_len) == 0) {
fprintf(stderr, "ERROR: EVP_CipherFinal_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto cleanup;
}
dbg("u_len = %d, f_len = %d\n", u_len, f_len);
// Write the final block, if any
if(f_len) {
dbg("Writing final %d bytes to %s...\n", f_len, outfile);
if(write(ofh, outbuf, f_len) != f_len) {
rc = AES_ERR_IO;
fprintf(stderr, "ERROR: Final write to the file %s failed. errno = %s\n", outfile, strerror(errno));
goto cleanup;
}
dbg("\tWrote last %d bytes\n", f_len);
}
rc = u_len + f_len;
cleanup:
EVP_CIPHER_CTX_cleanup(&ctx);
if(ifh != -1) close(ifh);
if(ofh != -1) close(ofh);
return rc;
}
This code is part of a little tool I wrote for fun, while waiting for my laundry in Brooklyn, called CryptoManiac. It depends on a couple of things like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#define AES_ERR_FILE_OPEN -1
#define AES_ERR_CIPHER_INIT -2
#define AES_ERR_CIPHER_UPDATE -3
#define AES_ERR_CIPHER_FINAL -4
#define AES_ERR_IO -5
#define BUF_SIZE (1024*1024)
#ifdef DEBUG
#define dbg(...) { fprintf(stderr, " %s: ", __FUNCTION__); \
fprintf(stderr, __VA_ARGS__); fflush(stderr); }
#else
#define dbg(...)
#endif
You can download the full C source code from Github.
Enjoy!
This post used to be at
http://alinush.org/2012/08/25/encrypting-a-file-using-aes-in-256-bit-cbcctr-mode-using-the-openssl-library/
.