1

I'm using openssl trying to use an RSA public key, to encrypt an AES key, and use that AES to send large-ish data over HTTP(s) to a 3rd party site. I know that's a lot of encryption, the second layer comes when the network is down, and the data has to be cached on disk until it can be POSTed.

I've been using the example code from this blog, a chunk of which is inlined below:

int aes_init(unsigned char *key_data, int key_data_len, unsigned char *salt, EVP_CIPHER_CTX *e_ctx)
{
  int i, nrounds = 5;
  unsigned char key[32], iv[32];
  /*
   * Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material.
   * nrounds is the number of times the we hash the material. More rounds are more secure but
   * slower.
   */
  i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
  if (i != 32) {
    printf("Key size is %d bits - should be 256 bits\n", i);
    return -1;
  }
  for(int x = 0; x<32; ++x)
    printf("Key: %x iv: %x \n", key[x], iv[x]);
  for(int x = 0; x<8; ++x)
    printf("salt: %x\n", salt[x]);
  EVP_CIPHER_CTX_init(e_ctx);
  EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
  return 0;
}

I'd like to stick with his aes_init() function, but I can't find a way to get the key out of the EVP_CIPHER_CTX once it has been initialised.

apropos lists a few functions relating to EVP_CIPHER_CTX:

$ apropos EVP_CIPHER_CTX
EVP_CIPHER_CTX_block_size (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_cipher (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_cleanup (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_ctrl (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_flags (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_get_app_data (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_init (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_iv_length (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_key_length (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_mode (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_nid (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_set_app_data (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_set_key_length (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_set_padding (3ssl) - EVP cipher routines
EVP_CIPHER_CTX_type (3ssl) - EVP cipher routines

EVP_CIPHER_CTX_set_key_length looks promising, but is there then some magical offset from which I have to read the EVP_CIPHER_CTX? Otherwise, I'll have to modify his function to return the key (and the iv), or throw away the function and inline the code.

The end goal here is to encrypt a large portion of data using AES, and encrypt the AES key using our RSA public key, base64 encode both, and broadcast them to the server. (Which I believe is the correct way of doing things)

The only issue then, is extracting the key from the EVP_CIPHER_CTX.

Lee Hambley
  • 6,270
  • 5
  • 49
  • 81
  • Why key and iv are not key and iv which you are looking for? – doptimusprime Apr 14 '13 at 11:44
  • I just need enough to decrypt it on the other side – Lee Hambley Apr 14 '13 at 13:46
  • Do I just need key_data and salt? – Lee Hambley Apr 14 '13 at 13:46
  • Then you need key and iv to be transferred securely to other side. You same key and iv at other side will do the work. Since you are using a function EVP_BytesToKey, you can use key_data and salt on the other side for decryption. Either key_data and salt or key and iv you need on other side securely. – doptimusprime Apr 14 '13 at 14:05
  • Thanks, post it as an answe and I'll be happy to accept when I've been able to make the code work. How much of a problem will random padding make? The other side needs. key_data, salt and the nrounds value, what about the padding that the EVP routines use? – Lee Hambley Apr 14 '13 at 14:11
  • I think that EVP routines handle the padding internally. You may not need to worry about it. Once, it works for you, please tell me. Then I will post it as an answer. – doptimusprime Apr 14 '13 at 14:42

1 Answers1

2

Why would you want to build your own solution to this kind of hybrid cryptography? There are already existing standards and methods that can help you.

I recommend you look into the PKCS#7 standard, which is the basis for S/MIME. OpenSSL has a direct interface to it. You tell it which data you want to encrypt using an asymmetric key and it handles the rest for you.

Look at pkcs7_encrypt and pkcs7_decrypt as well as the i2d_PKCS7_* functions for how to extract that data into a transmittable format (and d2i_PKCS7_* for reverse). See OpenSSL's documentation: PKCS7_encrypt, PKCS7_decrypt and you might want to familiarize yourself with the i2d/d2i convention used by OpenSSL (this is X509 but the d2i part applies here as well.

Edit: Here is an example for encryption (decryption is analogous):

#include <stdio.h>
#include <openssl/pem.h>
#include <openssl/crypto.h>
#include <openssl/err.h>


int main()
{
    STACK_OF(X509) *certs;
    FILE *fp;
    BIO *bio;
    PKCS7 *p7;

    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();

    certs = sk_X509_new_null();
    fp = fopen("cert.pem", "r");
    sk_X509_push(certs, PEM_read_X509(fp, NULL, NULL, NULL));
    fclose(fp);

    bio = BIO_new_file("data.txt", "r");
    p7 = PKCS7_encrypt(certs, bio, EVP_des_ede3_cbc(), 0);
    BIO_free(bio);

    bio = BIO_new_file("data.txt.enc", "w");
    i2d_PKCS7_bio(bio, p7);
    BIO_flush(bio);
    BIO_free(bio);

    ERR_print_errors_fp(stdout);
    return 0;
}

I have uploaded the full example to my repository.

Community
  • 1
  • 1
javex
  • 7,198
  • 7
  • 41
  • 60
  • Hi javex, thanks for the response. I appreciate the tip! I explored SMIME, but the idea of multi part posting, etc looked contrary to what I needed. If I can simplify my code, more the better. – Lee Hambley Apr 14 '13 at 20:00
  • I'm going in this direction after assuming I could use an RSA public key to encrypt a chunk of data of arbitrary size, and then being told by a colleague that the correct solution was to use RSA to encrypt an AES key, and encrypt my data with that key. I'd be much happier with a standard solution. – Lee Hambley Apr 14 '13 at 20:01
  • And, I'm inlining my `der/d2i` format key into the object files of my project, so I'm already part way down that road! – Lee Hambley Apr 14 '13 at 20:02
  • It's good if you found a better solution that works for you. But pay attention for potential problems on the road. The first that springs to mind: Don't use RSA v1.5 padding, use OAEP when you can. Also pay attention to not use ECB mode on the symmetric encryption. – javex Apr 14 '13 at 21:41
  • I didn't mean to belittle your solution, PKCS7 looks perfect, it looks like exactly what I was looking for, I meant to suggest that SMIME itself won't work for me, but the two base64 encoded chunks that one might normally see in an SMIME email would be perfect. – Lee Hambley Apr 15 '13 at 08:17
  • I have added some sample code for you. This works out of the box (see readme in repo) but you might want to alter it (and it needs checks & frees!). – javex Apr 15 '13 at 14:06
  • javex, excellent answer, thank you for taking the time. In my environment the key will be loaded from RAM, and the data also comes from RAM, so I'll need to rework the BIO related functions, but I have implementations of base64 encoding in-memory data from which I can borrow. – Lee Hambley Apr 15 '13 at 17:23
  • javex I wasn't able to verify the data on the command line, I can't find the correct incantations of the `openssl smime` command line to add them, can you assist? – Lee Hambley Apr 16 '13 at 22:36
  • Correction, I was able to retrieve the message using: `openssl smime -decrypt -in 1366150956.bin -recip cert.pem -inkey privkey.pem -inform DER` – Lee Hambley Apr 16 '13 at 22:53