Correct way to use php openssl_encrypt
Asked Answered
A

4

27

I'm working with cryptography on a project and I need a little help on how to work with openssl_encrypt and openssl_decrypt, I just want to know the most basic and correct way to do it. Here is what I got so far:

// To encrypt a string

$dataToEncrypt = 'Hello World';

$cypherMethod = 'AES-256-CBC';
$key = random_bytes(32);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cypherMethod));

$encryptedData = openssl_encrypt($dataToEncrypt, $cypherMethod, $key, $options=0, $iv);

I then store $cypherMethod, $key, and $iv for use when decrypting the $encryptedData. (Let's not elaborate how I store the values, thanks!)

// To decrypt an encrypted string

$decryptedData = openssl_decrypt($encryptedData, $cypherMethod, $key, $options=0, $iv);

First off, is the above example code a correct example of how to use php openssl_encrypt?

Second, is my method to generate the $key and $iv correct and secure? Because I keep on reading, keys should be cryptographically secure.

Lastly, isn't a 32-byte value required for AES-256-CBC? If yes, then why is it that openssl_cipher_iv_length() returns only int(16) as the length? Shouldn't it be int(32)?

Annis answered 29/12, 2017 at 5:32 Comment(1)
sir i am also working on cryptography, could you please elaborate how you store your values in database(mySql), which field types are suitable and in which format you did this. I can successfully encrypt text and store values for later decryption but openss_decrypt() method returns null( i think there is something wrong with storing data). Any help will be highly appreciatedVibrant
I
19

First off, is the above example code a correct example of how to use php openssl_encrypt?

Your usage of the function looks correct, however you may want to consider a mode of operation other than CBC. CBC is tricky to get right because just encrypting data in this mode has known attacks against it, such as the infamous CBC bit-flipping attack, which allows an attacker to make meaningful changes to the plaintext by modifying the ciphertext. If possible I would use an authenticated encryption mode like GCM if you can (looks like it's supported in PHP 7.1+ (Example #1)).

If you use CBC mode take a look at Example #2 in the docs. Note that after encrypting a MAC (message authentication code) is computed over the ciphertext and stored. This MAC should be recomputed before decrypting the ciphertext, and if it does not match the stored MAC then the ciphertext has been modified and is invalid.

Second, is my method to generate the $key and $iv correct and secure? Because I keep on reading, keys should be cryptographically secure.

Keys need to be generated using a cryptographically secure random number generator. Luckily, most operating systems provide one out of the box via /dev/urandom. This answer does a good job explaining how to read from /dev/urandom in PHP. openssl_random_pseudo_bytes should also be cryptographically secure but there are times when this is not the case.

Initialization Vectors (IVs) need to be random and should never be reused with the same key.

Lastly, isn't a 32-byte value required for AES-256-CBC? If yes, then why is it that openssl_cipher_iv_length() returns only int(16) as the length? Shouldn't it be int(32)?

AES is a block cipher that works on 128 bit (16 byte) blocks, regardless of key size.

Involucrum answered 29/12, 2017 at 23:30 Comment(2)
Thanks for the informative input. Just a quick question, should I use a single general use Key or should I also randomly generate it every time?Annis
You can use the same key, just be sure to generate a fresh random IV each time you call openssl_encrypt.Involucrum
S
17

Here is the most basic way to use openssl_encrypt and openssl_decrypt. Make sure to create 32 byte secret_key and 16 byte secret_iv

function encrypt_decrypt($action, $string) 
    {
        $output = false;
        $encrypt_method = "AES-256-CBC";
        $secret_key = 'xxxxxxxxxxxxxxxxxxxxxxxx';
        $secret_iv = 'xxxxxxxxxxxxxxxxxxxxxxxxx';
        // hash
        $key = hash('sha256', $secret_key);    
        // iv - encrypt method AES-256-CBC expects 16 bytes 
        $iv = substr(hash('sha256', $secret_iv), 0, 16);
        if ( $action == 'encrypt' ) {
            $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
            $output = base64_encode($output);
        } else if( $action == 'decrypt' ) {
            $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
        }
        return $output;
    }
Slattern answered 18/2, 2020 at 14:38 Comment(0)
B
4
// --- Encrypt --- //
$key = openssl_digest("passkey", 'SHA256', TRUE);
$plaintext = "Data to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
// binary cipher
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, OPENSSL_RAW_DATA, $iv);
// or replace OPENSSL_RAW_DATA & $iv with 0 & bin2hex($iv) for hex cipher (eg. for transmission over internet)

// or increase security with hashed cipher; (hex or base64 printable eg. for transmission over internet)
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );

// --- Decrypt --- //
$c = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, true);
if (hash_equals($hmac, $calcmac))
    echo $original_plaintext."\n";
Bollay answered 23/8, 2020 at 9:37 Comment(2)
Why do you get the key from a sha256-hashed string and not from a key derivation method like PBKDF2 or Argon2? When appending a hmac why not using the GCM mode instead?Mellie
There's always a more secure way; this example illustrates one way to encrypt data for storage & transmission. A better cryptography is public/private keys, instead of the one key for both encrypt & decrypt. See: #4630037Bollay
L
3
   // --- Encrypt --- //
function encrypt($plaintext, $secret_key = "5fgf5HJ5g27", $cipher = "AES-128-CBC")
{

    $key = openssl_digest($secret_key, 'SHA256', TRUE);

    $ivlen = openssl_cipher_iv_length($cipher);
    $iv = openssl_random_pseudo_bytes($ivlen);
    // binary cipher
    $ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, OPENSSL_RAW_DATA, $iv);
    // or replace OPENSSL_RAW_DATA & $iv with 0 & bin2hex($iv) for hex cipher (eg. for transmission over internet)

    // or increase security with hashed cipher; (hex or base64 printable eg. for transmission over internet)
    $hmac = hash_hmac('sha256', $ciphertext_raw, $key, true);
    return base64_encode($iv . $hmac . $ciphertext_raw);
}


// --- Decrypt --- //
function decrypt($ciphertext, $secret_key = "5fgf5HJ5g27", $cipher = "AES-128-CBC")
{

    $c = base64_decode($ciphertext);

    $key = openssl_digest($secret_key, 'SHA256', TRUE);

    $ivlen = openssl_cipher_iv_length($cipher);

    $iv = substr($c, 0, $ivlen);
    $hmac = substr($c, $ivlen, $sha2len = 32);
    $ciphertext_raw = substr($c, $ivlen + $sha2len);
    $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, OPENSSL_RAW_DATA, $iv);

    $calcmac = hash_hmac('sha256', $ciphertext_raw, $key, true);
    if (hash_equals($hmac, $calcmac))
        return $original_plaintext . "\n";
}
Laicize answered 6/9, 2021 at 9:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.