1

I want to use OpenSSL to encrypt / decrypt database data. I've found and tried using the code at the following link: How to encrypt plaintext with AES-256 CBC in PHP using OpenSSL?

However, it looks as though I need to keep the encryption_key & the initialization_vector for later use, if I want to be able to decrypt the data that's been encrypted at a later stage. This gives me a problem in that I can't find a way to store those bits of data. They are randomly generated when I encrypt the data and I currently lose them after that point, because it looks like I've got no way of storing them. I can store them to a PHP variable, but can't store them anywhere else on a long term basis.

I am aiming to encrypt all of the data in my clients database, so obviously need those bits of data to decrypt the data when necessary. I'll also need to use them when adding new data or editing existing data in the database.

I therefore need to find a way of storing the encryption_key & the initialization_vector for later use. They are both arrays of bytes (32 bytes & 16 bytes), but I can't find a way of storing them other than to a PHP variable.

Any help would be greatly appreciated.

craig-c
  • 161
  • 2
  • 5
  • Might be of interest: [Where to store a server side encryption key?](https://security.stackexchange.com/questions/12332/where-to-store-a-server-side-encryption-key) Also, [How to store the encryption keys securely in php code file](https://stackoverflow.com/questions/8959481/how-to-store-the-encryption-keys-securely-in-php-code-file). Probably a plethora of others if you search using some of those same keywords. – ficuscr Jul 23 '19 at 18:40
  • The main problem I have is rebuilding the encryption_key & the initialization_vector, which need to be arrays of bytes (encryption_key is 32 bytes + initialization_vector is 16 bytes). I can get the data out of these fields when they are created, but I need to rebuild them at a later stage, so that I can use them to encrypt/decrypt with the same key and vector. I just need to find out how to rebuild them with the required data. That's my main problem. it looks like I need to use PHPs pack/unpack functions, but I can't see how to use them to create bytes containing binary or decimal data. – craig-c Jul 24 '19 at 23:05

1 Answers1

2

However, it looks as though I need to keep the encryption_key & the initialization_vector for later use, if I want to be able to decrypt the data that's been encrypted at a later stage.

Please be careful to not confuse the two.

  • You will store one key, total, for your system. Keys can be reused for multiple messages.
  • Initialization vectors must be unique and unpredictable for each message.

That doesn't even get into the other problems (CBC mode isn't IND-CCA2 secure, padding oracle attacks against PKCS#7 padding, and a lot of implementation flaws that can occur in trying to remedy this by constructing a CBC+HMAC protocol).

How to Securely Solve the Problem

The best thing to do here is to make it someone else's problem and use a library that solves this problem for you. As security experts say, "Don't roll your own crypto."

CipherSweet is a PHP library (with a Node.js port) meant to facilitate searchable symmetric encryption. You don't have to use the searchable bits (it involves creating a blind index derived from the plaintext), the encryption is fine-tuned for security and easy to use.

See here for example

<?php
use ParagonIE\CipherSweet\CipherSweet;
use ParagonIE\CipherSweet\EncryptedField;
use ParagonIE\CipherSweet\EncryptedRow;
use ParagonIE\CipherSweet\KeyProvider\StringProvider;

// Set up CipherSweet; you can use other key providers (e.g. for Amazon KMS)
$provider = new StringProvider(
    // Example key, chosen randomly, hex-encoded:
    '4e1c44f87b4cdf21808762970b356891db180a9dd9850e7baf2a79ff3ab8a2fc'
);
$engine = new CipherSweet($provider);

// For just a single field
$fieldEncrypter = new EncryptedField($engine, 'my_table', 'my_field_name');
$ciphertext = $fieldEncrypter->encryptValue("some plaintext value");

// For multiple fields, this API is probably easier:
$rowEncrypter = (new EncryptedRow($engine, 'other_table'))
    ->addTextField('name')
    ->addIntegerField('hidden_id')
    ->addFloatField('latitude')
    ->addFloatField('longitude')
    ->addBooleanField('secret_boolean');

$safeToStore = $rowEncrypt->encryptRow([
   'name' => 'John',
   'hidden_id' => 123,
   'latitude' => 12.34,
   'longitude' => 56.789,
   'secret_boolean' => true
]);

It's free, open source, permissively licensed, developed by a company that specializes in application security and cryptography, and is well suited to the problem at hand.

The cool thing about CipherSweet is, if you use it, you don't have to ponder over the intrinsic differences between byte arrays and strings (and the conversion logic betwixt the two). You don't have to know what an IV even is, let alone how to use them correctly. The cryptography engineering is already done for you.

If you need to generate a key, just copy the StringProvider example above and replace the hex-encoded string with the output of bin2hex(random_bytes(32)).

Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206