0

I am writing a small class to add to my application that will handle things like config secrets. (I don't want to get into this topic as a lot of research has gone into the approach we've decided to take for this.)

One of the requirements is to encrypt file secrets. (Think a TLS certificate key or PGP secret key.) I am able to generate (and decrypt) a secret key that can be used to encrypt/decrypt the files outside of the application using the AWS KMS service. (See this for information about what this actually returns: https://docs.aws.amazon.com/cli/latest/reference/kms/generate-data-key.html)

So, to my way of thinking that just means I need to store the encrypted version of the key along with the file so that I can decrypt the key, use it to decrypt the file when it is used.

My confusion comes from the use of the openssl encrypt/decrypt functions on the file data due to the IV. I've read a lot about IV over the last couple of days and understand its general purpose, but am left with a number of questions attempting to reconcile this with what I see in the "real world".

When I use openssl from a command line to encrypt a file I've read to use this:

openssl aes-256-cbc -salt -in secrets.txt -out secrets.txt.enc

Notice there is no IV passed in, though the -salt parameter may serve a similar purpose?

But when I decrypt the same file I use a command like this:

openssl aes-256-cbc -d -in secrets.txt.enc -out secrets.txt.new

Notice no -salt parameter, no IV, or anything of that nature, so how is it able to decrypt the file without that information?

In the openssl encrypt/decrypt functions in PHP, and other discussions around IV, it sounds highly recommended to use an IV when encrypting data, which then suggests I need to store/use the IV when decrypting it as well, but this isn't being done from the command line, so how is that working? Is the command line above less secure for that? Do I not need to use an IV based on the fact that each file will have its own secure encryption key?

CameronGo
  • 13
  • 1
  • 3

2 Answers2

2

As stated in this thread

The OpenSSL developers preferred to derive the IV from the password, just like the key (i.e. they produce from the password a long sequence, which they split in two, one half being the encryption key, the other half being the IV).

The IV does not need to be provided for decryption since OpenSSL derives it from the password. The thread also mentions some drawbacks of using OpenSSL's file encryption method which is worth considering, like not having a MAC to verify the integrity of the encrypted file.

ARau
  • 619
  • 4
  • 9
  • OK, so based on that explanation it sounds like the IV is certainly required (regardless of how you arrive at one). Since the PHP functions for openssl encrypt/decrypt do not expose a similar salt parameter, then the IV would have to be arrived at independently and somehow communicated to the decryption process as well. Would using something like the hex of the filename as the IV for each file seem sufficient? At least this way no new information has to be communicated with the file. – CameronGo Sep 02 '16 at 16:35
  • That is correct. I am not a PHP expert by any means but in the user contributed documentation section in PHP documentation for OpenSSL here: http://php.net/manual/en/function.openssl-encrypt.php#104438 has the necessary code on how to pass the IV and key to the OpenSSL command line in order to decrypt the file. – ARau Sep 02 '16 at 16:47
  • I thought I would also post some info on how I actually implemented my solution. Your direction was helpful in clarifying that I wasn't missing something in my interpretation of the requirement for IV. What I ended up doing is similar to what OpenSSL command does with their salt; I just wrote the IV into the first X bytes of the new file followed by the encrypted data. I will try to post my actual function. – CameronGo Sep 21 '16 at 13:20
1

Here is the PHP function I implemented:

public function encryptFile($plainfile, $keyid) {
    $plaindata = file_get_contents($plainfile);
    $encfile = $plainfile.'.enc';
    $kms = KmsClient::factory(array(
        'region' => $this->kms_region,
        'version' => $this->kms_version
    ));

    $kmsresponse = $kms->generateDataKey(array(
        'KeyId'=>$keyid,
        'KeySpec'=>'AES_256',
    ));
    $key = $kmsresponse->get('Plaintext'); // Temporarily used only NEVER STORE THIS ANYWHERE
    $cipherkey = base64_encode($kmsresponse->get('CiphertextBlob'));
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cipheralgo));
    $encdata = openssl_encrypt($plaindata,$this->cipheralgo,$key,OPENSSL_RAW_DATA,$iv);

    // This writes the first few bytes of the file as the IV and then the reast is appended as encrypted data.  Decryption expects to find the file in this pattern.
    file_put_contents($encfile,$iv);
    file_put_contents($encfile,$encdata,FILE_APPEND);

    $response = json_encode(array('encfile'=>$encfile,'cipherkey'=>$cipherkey));
    return $response;
}
CameronGo
  • 13
  • 1
  • 3