What is an openssl iv, and why do I need a key and

2020-08-09 09:01发布

问题:

I am about to use the following script to encrypt and decrypt some data. I am using it because my current encryption does not work on our new server. We are currently using mcrypt so I want to change to openssl.

In our database we use aes encryption which uses a 128bit key so I know what a key is, but I do not know what an openssl iv is? And why would I need a key and an iv.

The code I am about to use is this, which I found on a website because I don't understand encryption very well.

Obviously I will modify it so that the key is kept somewhere else.

function encrypt_decrypt($action, $string) {
    $output = false;

    $encrypt_method = "AES-256-CBC";
    $secret_key = 'This is my secret key';
    $secret_iv = 'This is my secret iv';

    // hash
    $key = hash('sha256', $secret_key);

    // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
    $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;
}

$plain_txt = "This is my plain text";
echo "Plain Text = $plain_txt\n";

$encrypted_txt = encrypt_decrypt('encrypt', $plain_txt);
echo "Encrypted Text = $encrypted_txt\n";

$decrypted_txt = encrypt_decrypt('decrypt', $encrypted_txt);
echo "Decrypted Text = $decrypted_txt\n";

if( $plain_txt === $decrypted_txt ) echo "SUCCESS";
else echo "FAILED";

echo "\n";

回答1:

The Initialization Vector is part of what makes AES in CBC (Cipher Block Chaining) mode work - IVs are not unique to OpenSSL. CBC works by XORing the previous block with the current block. The very first block has no previous block, so the IV serves that purpose.

Why this is necessary requires a bit of understanding of how block ciphers work. Without this chaining and IV, we're left with a mode of AES called ECB, or Electronic Code Book. ECB has weaknesses that allow a chosen plaintext attack, among many other problems.

I would recommend spending a bit of time with best practices for CBC initialization vectors. Using them incorrectly can weaken the overall security of AES. The short explanation is:

  • IVs should be random and generated by a CSPRNG.
  • IVs should not be reused. That is, don't encrypt plaintext "A" and plaintext "B" with the same IV. Every record should have its own IV.
  • The IV is not a secret like the key. It can be stored in plaintext along with the cipher text.

Also keep in mind that this advice only applies to AES-CBC. If you ever investigate other modes of AES, such as GCM, this does not apply.



回答2:

I think you maybe mixed up "hashed key" and "iv" (God knows I did when starting crypto). hashed key is exactly what you did. For iv you should use different random data each time encryption is made with the same key. (my background: I had to build a pdo secure session handler which encrypts/decrypts the session data so I ended up implementing AES-256-CBC using openssl extension)

just a small tip if anyone gets here.

// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', $secret_iv), 0, 16);

is not the right way of generating iv + no need for secret_iv because iv has to be as random as it gets. Don't treat it (or understand it) the same as hashing.

As you have access to openssl extension there is a better/safer way of generating iv for the chosen cipher, openssl can also tell you the right length of the iv for the cipher:

$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('AES-256-CBC'));

It will be in binary format so if you need it in hex use bin2hex($iv). If you need to store the generated iv in mysql I store it in raw format (field type varbinary, binary also works).

One more thing. You have $options tag set to 0 in both openssl_encrypt and _decrypt which means "if set to true will return as raw output data, otherwise the return value is base64 encoded".



回答3:

Let's say two users have the password "princess", and you encode them with the key "some-key", they will both have the same encrypted result:

| User       | Encrypted password    |
|------------|-----------------------|
| a          | sN7vIFg=              |
| b          | sN7vIFg=              |

Which means that if someone figures out using a different means that user a's real password is "princess" and their encrypted password is "sN7vIFg=", then any user with the encrypted password "sN7vIFg=" can be deemed to have the password "princess".

However if you use a random IV for each, then it is harder to guess the underlying passwords if you have access to the encrypted data.

| User       | Encrypted password    | IV             |
|------------|-----------------------|----------------|
| a          | sN7vIFg=              | h²ÓIF8]h%L     |
| b          | BjZAzrA=              | VÂøíiøÚØò▓     |

Now, even if someone has access to the above data, they can't figure out that users a and b have the same password.

See also Is “real salt” the same as “initialization vectors”? .