3

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?

jww
  • 97,681
  • 90
  • 411
  • 885
dasj19
  • 177
  • 1
  • 3
  • 10
  • 3
    The IV needs to be 16 bytes. What does OpenSSL's [`ERR_get_error` return](https://secure.php.net/manual/en/function.openssl-error-string.php)? Maybe related, [Use of Initialization Vector in openssl_encrypt](http://stackoverflow.com/q/11821195/608639). – jww Jan 31 '17 at 09:32
  • 3
    Normally my first advice would be "read the documentation" but it turns out the PHP documentation for openssl_encrypt is pretty awful and barely even exists. – GordonM Jan 31 '17 at 09:35
  • 1
    You can use `openssl_error_string()` to find out what *exactly* went wrong. You can also use [openssl_cipher_iv_length](http://php.net/manual/en/function.openssl-cipher-iv-length.php) to compute the length of initialization vector for the selected cipher. Using the two, it's *easier* to determine what goes wrong. – Mjh Jan 31 '17 at 12:38

4 Answers4

4

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

Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
Mjh
  • 2,904
  • 1
  • 17
  • 16
0
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 ());
}
GordonM
  • 31,179
  • 15
  • 87
  • 129
0
  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.

zaph
  • 111,848
  • 21
  • 189
  • 228
0

Before start fixing this bug, check all extension which is required for openssl_encrypt/decrypt is enabled?

class AnswerEncryption
{
    const CURRENT_ALGO = 'AES-128-ECB';
    const CIPHER='A?N@G+KbPe778mYq3t6w9z$C&F!J@jcQ';
    CONST IV='1234567890123455';

    /**
     * @param null $Value
     * @param null $cipher
     * @return false|string
     */
    public static function Encrypt($Value=null){
        $iv = substr(self::IV, 0, 16);
        return (openssl_encrypt($Value,self::CURRENT_ALGO,self::CIPHER,0,$iv));
    }

    /**
     * @param null $Value
     * @return int
     */
    public static function Decrypt($Value=null): int
    {
        $iv = substr(self::IV, 0, 16);
        return intval(openssl_decrypt($Value,self::CURRENT_ALGO,self::CIPHER,0,$iv));
    }

}

in the decrypt method, I want the integer value, so you can change it accordingly

Kishor Pant
  • 136
  • 1
  • 4