openssl_encrypt returns false

2019-05-10 09:46发布

问题:

I am trying to encrypt a string using openssl_encrypt in PHP but it keeps returning FALSE.

$encrypted = openssl_encrypt('1234', 'AES-256-CBC', 'kGJeGF2hEQ', OPENSSL_ZERO_PADDING, '1234123412341234');

What am I doing wrong?

回答1:

On top of answers posted, which are excellent, the code you're after, given your input parameters would be the following:

$plaintext = '1234';
$cipher = 'AES-256-CBC';
$key = 'this is a bad key';
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));

$encrypted = openssl_encrypt($plaintext, $cipher, $key, 0, $iv);

if(false === $encrypted)
{
    echo openssl_error_string();
    die;
}

$decrypted = openssl_decrypt($encrypted, $cipher, $key, 0, $iv);

$result = $decrypted === $plaintext;

print $result ? 'Everything is fine' : 'Well, we did not decrypt good, did we?';

Having written the above, I advise against using it and instead, please use a tested library designed to handle the complexities of encryption and decryption for you.

I suggest using defuse/php-encryption



回答2:

php > var_dump (openssl_encrypt('1234', 'AES-256-CBC', 'kGJeGF2hEQ', OPENSSL_ZERO_PADDING, '1234123412341234'));
php shell code:1:
bool(false)
php > var_dump (openssl_error_string ());
php shell code:1:
string(94) "error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length"

It seems that the cypher you're using requires that the data you're encrypting has a length that's an exact multiple of the block length. With some experimentation I found that 1234123412341234 is successfully encrypted.

I don't know if this is a universal feature of all openssl encryption schemes, or whether it's something that's specific to certain schemes. In the former case you'll need to pad the input to a multiple of the block size. If the latter is true then you can either pad, or switch to a different encryption scheme that doesn't impose the same restrictions on the input.

For padding you need to find out what the blocksize of your chosen cypher is (I don't know if there's an openssl function or constant provided for that), then work out how many characters you need to pad your input string by.

Note that the following example assumes that a) there's some way of getting the blocksize programmatically (if not then you'll have to hard-code that yourself) and b) you're working with a byte-oriented character format (unicode might cause issues!)

$plaintext = "Hello, I'm some plaintext!";
$blocksize = function_that_gets_a_blocksize_for_a_given_cypher ($cypher);
$strlen = strlen ($plaintext);
$pad = $blocksize - ($strlen % $blocksize);

// If the string length is already a multiple of the blocksize then we don't need to do anything
if ($pad === $blocksize) {
    $pad = 0;
}

$plaintext = str_pad ($plaintext, $strlen + $pad);

As for your code, this suggests you need to implement some error detection into it (but be careful what you actually log/echo out as part of the error detection!).

$encrypted = openssl_encrypt('1234', 'AES-256-CBC', 'kGJeGF2hEQ', OPENSSL_ZERO_PADDING, '1234123412341234');
if (false === $encrypted) {
    error_log ("Encryption failed! " . openssl_error_string ());
}


回答3:

  1. Since block ciphers such as AES require input data to be an exact multiple of the block size (16-bytes for AES) padding is necessary. The usual method is just to specify PKCS#7 (née PKCS#5) by passing it as an option and the padding will be automatically added on encryption and removed on decryption. Zero padding (OPENSSL_ZERO_PADDING) is a poor solution since it will not work for binary data.

  2. The IV needs to be block size, 8-bytes for AES. Do not rely on the implementation for padding.

  3. The key should be the exact size specified, valid block sizes foe AES are 128, 192 or 256 bits (16, 24 or 32 bytes). Do not rely on the implementation for padding.