openssl_encrypt

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

openssl_encryptデータを暗号化する

説明

openssl_encrypt(
    string $data,
    string $cipher_algo,
    string $passphrase,
    int $options = 0,
    string $iv = "",
    string &$tag = null,
    string $aad = "",
    int $tag_length = 16
): string|false

与えられた文字列を与えられたメソッドとキーで暗号化して、 未加工の、または base64 エンコードされた文字列を返します。

パラメータ

data

暗号化するプレーンテキストメッセージ

cipher_algo

暗号メソッド。 使用可能なメソッドの一覧を取得するには、openssl_get_cipher_methods() を用います。

passphrase

パスフレーズ。 期待された長さより短かった場合は、 黙ってNUL 文字で埋められます。 期待された長さより長かった場合は、 黙って切り詰められます。

options

OPENSSL_RAW_DATAOPENSSL_ZERO_PADDING のビット OR。

iv

NULL ではない初期化ベクトル。

tag

AEAD 暗号モード (GCM または CCM) を使う場合の 認証タグをリファレンスで渡します。

aad

追加の認証済みデータ

tag_length

認証 tag の長さ。 GCMモードでは、この値は 4 から 16 の間です。

戻り値

成功した場合暗号化された文字列、失敗した場合に false を返します。

エラー / 例外

cipher_algo パラメータを通じて未知の暗号アルゴリズムが渡された場合、 E_WARNING レベルのエラーを発生します。

iv パラメータを通じて空値が渡された場合、 E_WARNING レベルのエラーを発生します。

変更履歴

バージョン 説明
7.1.0 tagaad および tag_length パラメータが追加されました。

例1 PHP 7.1以降で、AES を GCMモードで使う例

<?php
//$key should have been previously generated in a cryptographically safe way, like openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$cipher = "aes-128-gcm";
if (
in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
//store $cipher, $iv, and $tag for decryption later
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv, $tag);
echo
$original_plaintext."\n";
}
?>

例2 PHP 7.1 より前のバージョンで、AES を使う例

<?php
//$key previously generated safely, ie: openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );

//decrypt later....
$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, $options=OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
if (
hash_equals($hmac, $calcmac))// timing attack safe comparison
{
echo
$original_plaintext."\n";
}
?>

参考

add a note add a note

User Contributed Notes 26 notes

up
48
Nick
8 years ago
There's a lot of confusion plus some false guidance here on the openssl library.

The basic tips are:

aes-256-ctr is arguably the best choice for cipher algorithm as of 2016. This avoids potential security issues (so-called padding oracle attacks) and bloat from algorithms that pad data to a certain block size. aes-256-gcm is preferable, but not usable until the openssl library is enhanced, which is due in PHP 7.1

Use different random data for the initialisation vector each time encryption is made with the same key. mcrypt_create_iv() is one choice for random data. AES uses 16 byte blocks, so you need 16 bytes for the iv.

Join the iv data to the encrypted result and extract the iv data again when decrypting.

Pass OPENSSL_RAW_DATA for the flags and encode the result if necessary after adding in the iv data.

Hash the chosen encryption key (the password parameter) using openssl_digest() with a hash function such as sha256, and use the hashed value for the password parameter.

There's a simple Cryptor class on GitHub called php-openssl-cryptor that demonstrates encryption/decryption and hashing with openssl, along with how to produce and consume the data in base64 and hex as well as binary. It should lay the foundations for better understanding and making effective use of openssl with PHP.

Hopefully it will help anyone looking to get started with this powerful library.
up
41
omidbahrami1990 at gmail dot com
6 years ago
This Is The Most Secure Way To Encrypt And Decrypt Your Data,
It Is Almost Impossible To Crack Your Encryption.
--------------------------------------------------------
--- Create Two Random Keys And Save Them In Your Configuration File ---
<?php
// Create The First Key
echo base64_encode(openssl_random_pseudo_bytes(32));

// Create The Second Key
echo base64_encode(openssl_random_pseudo_bytes(64));
?>
--------------------------------------------------------
<?php
// Save The Keys In Your Configuration File
define('FIRSTKEY','Lk5Uz3slx3BrAghS1aaW5AYgWZRV0tIX5eI0yPchFz4=');
define('SECONDKEY','EZ44mFi3TlAey1b2w4Y7lVDuqO+SRxGXsa7nctnr/JmMrA2vN6EJhrvdVZbxaQs5jpSe34X3ejFK/o9+Y5c83w==');
?>
--------------------------------------------------------
<?php
function secured_encrypt($data)
{
$first_key = base64_decode(FIRSTKEY);
$second_key = base64_decode(SECONDKEY);   
   
$method = "aes-256-cbc";   
$iv_length = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($iv_length);
       
$first_encrypted = openssl_encrypt($data,$method,$first_key, OPENSSL_RAW_DATA ,$iv);   
$second_encrypted = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);
           
$output = base64_encode($iv.$second_encrypted.$first_encrypted);   
return
$output;       
}
?>
--------------------------------------------------------
<?php
function secured_decrypt($input)
{
$first_key = base64_decode(FIRSTKEY);
$second_key = base64_decode(SECONDKEY);           
$mix = base64_decode($input);
       
$method = "aes-256-cbc";   
$iv_length = openssl_cipher_iv_length($method);
           
$iv = substr($mix,0,$iv_length);
$second_encrypted = substr($mix,$iv_length,64);
$first_encrypted = substr($mix,$iv_length+64);
           
$data = openssl_decrypt($first_encrypted,$method,$first_key,OPENSSL_RAW_DATA,$iv);
$second_encrypted_new = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);
   
if (
hash_equals($second_encrypted,$second_encrypted_new))
return
$data;
   
return
false;
}
?>
up
48
biohazard dot ge at gmail dot com
13 years ago
Many users give up with handilng problem when openssl command line tool cant decrypt php openssl encrypted file which is encrypted with openssl_encrypt function.

For example how beginner is encrypting data:

<?php

$string
= 'It works ? Or not it works ?';
$pass = '1234';
$method = 'aes128';

file_put_contents ('./file.encrypted', openssl_encrypt ($string, $method, $pass));

?>

And then how beginner is trying to decrypt data from command line:

# openssl enc -aes-128-cbc -d -in file.encrypted -pass pass:123

Or even if he/she determinates that openssl_encrypt output was base64 and tries:

# openssl enc -aes-128-cbc -d -in file.encrypted -base64 -pass pass:123

Or even if he determinates that base64 encoded file is represented in one line and tries:

# openssl enc -aes-128-cbc -d -in file.encrypted -base64 -A -pass pass:123

Or even if he determinates that IV is needed and adds some string iv as encryption function`s fourth parameter and than adds hex representation of iv as parameter in openssl command line :

# openssl enc -aes-128-cbc -d -in file.encrypted -base64 -pass pass:123 -iv -iv 31323334353637383132333435363738

Or even if he determinates that aes-128 password must be 128 bits there fore 16 bytes and sets $pass = '1234567812345678' and tries:

# openssl enc -aes-128-cbc -d -in file.encrypted -base64 -pass pass:1234567812345678 -iv -iv 31323334353637383132333435363738

All these troubles will have no result in any case.

BECAUSE THE PASSWORD PARAMETER DOCUMENTED HERE IS NOT THE PASSWORD.

It means that the password parameter of the function is not the same string used as [-pass pass:] parameter with openssl cmd tool for file encryption decryption.

IT IS THE KEY !

And now how to correctly encrypt data with php openssl_encrypt and how to correctly decrypt it from openssl command line tool.

<?php

   
function strtohex($x)
    {
       
$s='';
        foreach (
str_split($x) as $c) $s.=sprintf("%02X",ord($c));
        return(
$s);
    }
   
   
$source = 'It works !';

   
$iv = "1234567812345678";
   
$pass = '1234567812345678';
   
$method = 'aes-128-cbc';

    echo
"\niv in hex to use: ".strtohex ($iv);
    echo
"\nkey in hex to use: ".strtohex ($pass);
    echo
"\n";

   
file_put_contents ('./file.encrypted',openssl_encrypt ($source, $method, $pass, true, $iv));

   
$exec = "openssl enc -".$method." -d -in file.encrypted -nosalt -nopad -K ".strtohex($pass)." -iv ".strtohex($iv);

    echo
'executing: '.$exec."\n\n";
    echo
exec ($exec);
    echo
"\n";

?>

IV and Key parameteres passed to openssl command line must be in hex representation of string.

The correct command for decrypting is:

# openssl enc -aes-128-cbc -d -in file.encrypted -nosalt -nopad -K 31323334353637383132333435363738 -iv 31323334353637383132333435363738

As it has no salt has no padding and by setting functions third parameter we have no more base64 encoded file to decode. The command will echo that it works...

: /
up
35
openssl at mailismagic dot com
9 years ago
Since the $options are not documented, I'm going to clarify what they mean here in the comments.  Behind the scenes, in the source code for /ext/openssl/openssl.c:

    EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
    if (options & OPENSSL_ZERO_PADDING) {
        EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0);
    }

And later:

        if (options & OPENSSL_RAW_DATA) {
            outbuf[outlen] = '\0';
            RETVAL_STRINGL((char *)outbuf, outlen, 0);
        } else {
            int base64_str_len;
            char *base64_str;

            base64_str = (char*)php_base64_encode(outbuf, outlen, &base64_str_len);
            efree(outbuf);
            RETVAL_STRINGL(base64_str, base64_str_len, 0);
        }

So as we can see here, OPENSSL_ZERO_PADDING has a direct impact on the OpenSSL context.  EVP_CIPHER_CTX_set_padding() enables or disables padding (enabled by default).  So, OPENSSL_ZERO_PADDING disables padding for the context, which means that you will have to manually apply your own padding out to the block size.  Without using OPENSSL_ZERO_PADDING, you will automatically get PKCS#7 padding.

OPENSSL_RAW_DATA does not affect the OpenSSL context but has an impact on the format of the data returned to the caller.  When OPENSSL_RAW_DATA is specified, the returned data is returned as-is.  When it is not specified, Base64 encoded data is returned to the caller.

Hope this saves someone a trip to the PHP source code to figure out what the $options do.  Pro developer tip:  Download and have a copy of the PHP source code locally so that, when the PHP documentation fails to live up to quality expectations, you can see what is actually happening behind the scenes.
up
17
naitsirch at e dot mail dot de
8 years ago
PHP lacks a build-in function to encrypt and decrypt large files. `openssl_encrypt()` can be used to encrypt strings, but loading a huge file into memory is a bad idea.

So we have to write a userland function doing that. This example uses the symmetric AES-128-CBC algorithm to encrypt smaller chunks of a large file and writes them into another file.

# Encrypt Files

<?php
/**
* Define the number of blocks that should be read from the source file for each chunk.
* For 'AES-128-CBC' each block consist of 16 bytes.
* So if we read 10,000 blocks we load 160kb into memory. You may adjust this value
* to read/write shorter or longer chunks.
*/
define('FILE_ENCRYPTION_BLOCKS', 10000);

/**
* Encrypt the passed file and saves the result in a new file with ".enc" as suffix.
*
* @param string $source Path to file that should be encrypted
* @param string $key    The key used for the encryption
* @param string $dest   File name where the encryped file should be written to.
* @return string|false  Returns the file name that has been created or FALSE if an error occured
*/
function encryptFile($source, $key, $dest)
{
   
$key = substr(sha1($key, true), 0, 16);
   
$iv = openssl_random_pseudo_bytes(16);

   
$error = false;
    if (
$fpOut = fopen($dest, 'w')) {
       
// Put the initialzation vector to the beginning of the file
       
fwrite($fpOut, $iv);
        if (
$fpIn = fopen($source, 'rb')) {
            while (!
feof($fpIn)) {
               
$plaintext = fread($fpIn, 16 * FILE_ENCRYPTION_BLOCKS);
               
$ciphertext = openssl_encrypt($plaintext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
               
// Use the first 16 bytes of the ciphertext as the next initialization vector
               
$iv = substr($ciphertext, 0, 16);
               
fwrite($fpOut, $ciphertext);
            }
           
fclose($fpIn);
        } else {
           
$error = true;
        }
       
fclose($fpOut);
    } else {
       
$error = true;
    }

    return
$error ? false : $dest;
}
?>

# Decrypt Files

To decrypt files that have been encrypted with the above function you can use this function.

<?php
/**
* Dencrypt the passed file and saves the result in a new file, removing the
* last 4 characters from file name.
*
* @param string $source Path to file that should be decrypted
* @param string $key    The key used for the decryption (must be the same as for encryption)
* @param string $dest   File name where the decryped file should be written to.
* @return string|false  Returns the file name that has been created or FALSE if an error occured
*/
function decryptFile($source, $key, $dest)
{
   
$key = substr(sha1($key, true), 0, 16);

   
$error = false;
    if (
$fpOut = fopen($dest, 'w')) {
        if (
$fpIn = fopen($source, 'rb')) {
           
// Get the initialzation vector from the beginning of the file
           
$iv = fread($fpIn, 16);
            while (!
feof($fpIn)) {
               
// we have to read one block more for decrypting than for encrypting
               
$ciphertext = fread($fpIn, 16 * (FILE_ENCRYPTION_BLOCKS + 1));
               
$plaintext = openssl_decrypt($ciphertext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
               
// Use the first 16 bytes of the ciphertext as the next initialization vector
               
$iv = substr($ciphertext, 0, 16);
               
fwrite($fpOut, $plaintext);
            }
           
fclose($fpIn);
        } else {
           
$error = true;
        }
       
fclose($fpOut);
    } else {
       
$error = true;
    }

    return
$error ? false : $dest;
}
?>

Source: http://stackoverflow.com/documentation/php/5794/cryptography/25499/
up
2
desmatic at gmail dot com
3 years ago
Upgraded php and needed something to replace insecure legacy mcrypt libs, but still supported classic user, password interface.

<?php
function encrypt($plaintext, $key, $cipher = "aes-256-gcm") {
    if (!
in_array($cipher, openssl_get_cipher_methods())) {
        return
false;
    }
   
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
   
$tag = null;
   
$ciphertext = openssl_encrypt(
       
gzcompress($plaintext),
       
$cipher,
       
base64_decode($key),
       
$options=0,
       
$iv,
       
$tag,
    );
    return
json_encode(
        array(
           
"ciphertext" => base64_encode($ciphertext),
           
"cipher" => $cipher,
           
"iv" => base64_encode($iv),
           
"tag" => base64_encode($tag),
        )
    );
}

function
decrypt($cipherjson, $key) {
    try {
       
$json = json_decode($cipherjson, true, 2JSON_THROW_ON_ERROR);
    } catch (
Exception $e) {
        return
false;
    }
    return
gzuncompress(
       
openssl_decrypt(
           
base64_decode($json['ciphertext']),
           
$json['cipher'],
           
base64_decode($key),
           
$options=0,
           
base64_decode($json['iv']),
           
base64_decode($json['tag'])
        )
    );
}

$secret = "MySecRet@123";
$cipherjson = encrypt("Hello world!\n", $secret);
echo
decrypt($cipherjson, $secret);

?>
up
7
TheNorthMemory
3 years ago
I saw that a doc bug(#80236) were there mentioned that $tag usage. Here is an examples, Hopes those may help someone.

<?php

/**
* Encrypts given data with given key, iv and aad, returns a base64 encoded string.
*
* @param string $plaintext - Text to encode.
* @param string $key - The secret key, 32 bytes string.
* @param string $iv - The initialization vector, 16 bytes string.
* @param string $aad - The additional authenticated data, maybe empty string.
*
* @return string - The base64-encoded ciphertext.
*/
function encrypt(string $plaintext, string $key, string $iv = '', string $aad = ''): string
{
   
$ciphertext = openssl_encrypt($plaintext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag, $aad, 16);

    if (
false === $ciphertext) {
        throw new
UnexpectedValueException('Encrypting the input $plaintext failed, please checking your $key and $iv whether or nor correct.');
    }

    return
base64_encode($ciphertext . $tag);
}

/**
* Takes a base64 encoded string and decrypts it using a given key, iv and aad.
*
* @param string $ciphertext - The base64-encoded ciphertext.
* @param string $key - The secret key, 32 bytes string.
* @param string $iv - The initialization vector, 16 bytes string.
* @param string $aad - The additional authenticated data, maybe empty string.
*
* @return string - The utf-8 plaintext.
*/
function decrypt(string $ciphertext, string $key, string $iv = '', string $aad = ''): string
{
   
$ciphertext = base64_decode($ciphertext);
   
$authTag = substr($ciphertext, -16);
   
$tagLength = strlen($authTag);

   
/* Manually checking the length of the tag, because the `openssl_decrypt` was mentioned there, it's the caller's responsibility. */
   
if ($tagLength > 16 || ($tagLength < 12 && $tagLength !== 8 && $tagLength !== 4)) {
        throw new
RuntimeException('The inputs `$ciphertext` incomplete, the bytes length must be one of 16, 15, 14, 13, 12, 8 or 4.');
    }

   
$plaintext = openssl_decrypt(substr($ciphertext, 0, -16), 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $authTag, $aad);

    if (
false === $plaintext) {
        throw new
UnexpectedValueException('Decrypting the input $ciphertext failed, please checking your $key and $iv whether or nor correct.');
    }

    return
$plaintext;
}

// usage samples
$aesKey = random_bytes(32);
$aesIv = random_bytes(16);
$ciphertext = encrypt('thing', $aesKey, $aesIv);
$plaintext = decrypt($ciphertext, $aesKey, $aesIv);

var_dump($ciphertext);
var_dump($plaintext);
up
6
Jean-Luc
7 years ago
Important: The key should have exactly the same length as the cipher you are using. For example, if you use AES-256 then you should provide a $key that is 32 bytes long (256 bits == 32 bytes). Any additional bytes in $key will be truncated and not used at all.
up
11
Anonymous
9 years ago
Just a couple of notes about the parameters:

data - It is interpreted as a binary string
method - Regular string, make sure you check openssl_get_cipher_methods() for a list of the ciphers available in your server*
password - As biohazard mentioned before, this is actually THE KEY! It should be in hex format.
options - As explained in the Parameters section
iv - Initialization Vector. Different than biohazard mentioned before, this should be a BINARY string. You should check for your particular implementation.

To verify the length/format of your IV, you can provide strings of different lengths and check the error log. For example, in PHP 5.5.9 (Ubuntu 14.04 LTS), providing a 32 byte hex string (which would represent a 16 byte binary IV) throws an error.
"IV passed is 32 bytes long which is longer than the 16 expected by the selected cipher" (cipher chosen was 'aes-256-cbc' which uses an IV of 128 bits, its block size).
Alternatively, you can use openssl_cipher_iv_length().

From the security standpoint, make sure you understand whether your IV needs to be random, secret or encrypted. Many times the IV can be non-secret but it has to be a cryptographically secure random number. Make sure you generate it with an appropriate function like openssl_random_pseudo_bytes(), not mt_rand().

*Note that the available cipher methods can differ between your dev server and your production server! They will depend on the installation and compilation options used for OpenSSL in your machine(s).
up
3
Shin
3 years ago
Concise description about "options" parameter!

http://phpcoderweb.com/manual/function-openssl-encrypt_5698.html

> OPENSSL_ZERO_PADDING has a direct impact on the OpenSSL context.  EVP_CIPHER_CTX_set_padding() enables or disables padding (enabled by default).  So, OPENSSL_ZERO_PADDING disables padding for the context, which means that you will have to manually apply your own padding out to the block size.  Without using OPENSSL_ZERO_PADDING, you will automatically get PKCS#7 padding.

> OPENSSL_RAW_DATA does not affect the OpenSSL context but has an impact on the format of the data returned to the caller.  When OPENSSL_RAW_DATA is specified, the returned data is returned as-is.  When it is not specified, Base64 encoded data is returned to the caller.

Where
- OPENSSL_RAW_DATA=1
- OPENSSL_ZERO_PADDING=2

Hence
options = 0
-> PKCS#7 padding, Base64 Encode
options = 1
-> PKCS#7 padding, No Base64 Encode (RAW DATA)
options = 2
-> No padding, Base64 Encode
options = 3  ( 1 OR 2 )
-> No padding, No Base64 Encode (RAW DATA)
up
12
denis at bitrix dot ru
7 years ago
How to migrate from mcrypt to openssl with backward compatibility.

In my case I used Blowfish in ECB mode. The task was to decrypt data with openssl_decrypt, encrypted by mcrypt_encrypt and vice versa. It was obvious for a first sight. But in fact openssl_encrypt and mcrypt_encript give different results in most cases.

Investigating the web I found out that the reason is in different padding methods. And for some reasons openssl_encrypt behave the same strange way with OPENSSL_ZERO_PADDING and OPENSSL_NO_PADDING options: it returns FALSE if encrypted string doesn't divide to the block size. To solve the problem you have to pad your string with NULs by yourself.

The second question was the key length. Both functions give the same result if the key length is between 16 and 56 bytes. And I managed to find that if your key is shorter than 16 bytes, you just have to repeat it appropriate number of times.

And finally the code follows which works the same way on openssl and mcrypt libraries.

<?php
   
function encrypt($data, $key)
    {
       
$l = strlen($key);
        if (
$l < 16)
           
$key = str_repeat($key, ceil(16/$l));

        if (
$m = strlen($data)%8)
           
$data .= str_repeat("\x00"8 - $m);
        if (
function_exists('mcrypt_encrypt'))
           
$val = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
        else
           
$val = openssl_encrypt($data, 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);

        return
$val;
    }

    function
decrypt($data, $key)
    {
       
$l = strlen($key);
        if (
$l < 16)
           
$key = str_repeat($key, ceil(16/$l));

        if (
function_exists('mcrypt_encrypt'))
           
$val = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
        else
           
$val = openssl_decrypt($data, 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
        return
$val;
    }

$data = 'my secret message';
$key = 'dontsay';

$c = encrypt($data, $key);
$d = decrypt($c, $key);
var_dump($c);
var_dump($d);
?>
Gives:

string(32) "SWBMedXJIxuA9FcMOqCqomk0E5nFq6wv"
string(24) "my secret message\000\000\000\000\000\000\000"
up
11
Raphael
9 years ago
Beware of the padding this method adds !

<?php
$encryption_key
= openssl_random_pseudo_bytes(32);
$iv = openssl_random_pseudo_bytes(16);
$data = openssl_random_pseudo_bytes(32);

for (
$i = 0; $i < 5; $i++) {
$data = openssl_encrypt($data, 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA, $iv);
echo
strlen($data) . "\n";
}
?>

With this sample the output will be:
48
64
80
96
112

This is because our $data is already taking all the block size, so the method is adding a new block which will contain only padded bytes.

The only solution that come to my mind to avoid this situation is to add the option OPENSSL_ZERO_PADDING along with the first one:
<?php
$data
= openssl_encrypt($data, 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
?>

/!\ Be careful when using this option, be sure that you provide data that have already been padded or that takes already all the block size.
up
8
max
12 years ago
Might be useful to people trying to use 'aes-256-cbc' cipher (and probably other cbc ciphers) in collaboration with other implementations of AES (C libs for example) that the openssl extension has a strict implementation regarding padding bytes. I found the solution only by manually going through the openssl source.

In C, you would want to pad plaintexts the following way (assuming all mem allocations are proper):

nPadding = ( 16 - ( bufferSize % 16 ) ) ? ( 16 - ( bufferSize % 16 ) ) : 16;
for( index = bufferSize; index < bufferSize + nPadding; index++ )
{
    plaintext[ index ] = (char)nPadding;
}

while decryptions are validated like:

isSuccess = TRUE;
for( index = bufferSize - 1; index > ( bufferSize - nPadding ); index-- )
{
    if( plaintext[ index ] != nPadding )
    {
        isSuccess = FALSE;
        break;
    }
}
decryptedSize = bufferSize - nPadding;

In plain english, the buffer must be padded up to blockSize. If the buffer is already a multiple of blockSize, you add an entire new blockSize bytes as padding.

The value of the padding bytes MUST be the number of padding bytes as a byte...

So 5 bytes of padding will result in the following bytes added at the end of the ciphertext:
[ 0x05 ][ 0x05 ][ 0x05 ][ 0x05 ][ 0x05 ]

Hope this saves someone else a few hours of their life.
up
2
gcleaves at gmail dot com
5 years ago
Please note that at the time of writing this, there is an important and naive security vulnerability in "Example #2 AES Authenticated Encryption example for PHP 5.6+".

You MUST include the IV when calculating the HMAC. Otherwise, somebody could alter the IV during transport, thereby changing the decrypted message while maintaining HMAC integrity. An absolute disaster.

To fix the example, the HMAC should be calculated like this:

<?php
$hmac
= hash_hmac('sha256', $iv.$ciphertext_raw, $key, $as_binary=true);
?>
up
1
Jess Portnoy
6 years ago
Note that OPENSSL_RAW_DATA and OPENSSL_ZERO_PADDING were introduced by this commit:
https://github.com/php/php-src/commit/9e7ae3b2d0e942b816e3836025456544d6288ac3

Before that, the options arg was called raw_output and was a Boolean so if you're considering this method as a replacement for mcrypt_encrypt(), this will only work with PHP 5.5 and above.

A good guide on replacing Mcrypt encryption/decryption methods with the OpenSSL parallels can be found here:
http://thefsb.tumblr.com/post/110749271235/using-opensslendecrypt-in-php-instead-of
up
1
darek334 at gazeta dot pl
7 years ago
To check if cipher uses IV use openssl_cipher_iv_length it returns length if exist, 0 if not, false if cipher is unknown.
up
1
Kruthers
8 years ago
There still seems to be some confusion about the "password" argument to this function.  It accepts a binary string for the key (ie. NOT encoded), at least for the cipher methods I tried (AES-128-CTR and AES-256-CTR).  One of the posts says you should hex encode the key (which is wrong), and some say you should hash the key but don't make it clear how to properly pass the hashed key.

Instead of the post made by anonymous, this should be more accurate info about the parameters:

data - BINARY string
method - regular string, from openssl_get_cipher_methods()
password - BINARY string (ie. the encryption key in binary)
options - integer (use the constants provided)
iv - BINARY string

This is not only from my testing, but backed up by the usage of this function by https://github.com/defuse/php-encryption
up
2
ralf at exphpert dot de
2 years ago
I'd like to point out that the command description doesn't very well point out, how the command really works for the less experienced user.

One important point is, that you do NOT pass a tag to openssl_encrypt. Any value in the tag variable will be overwritten by openssl_encrypt. It by itself will create a tag which you will need to store.

To be able to decrypt the encrypted secret with openssl_decrypt, you need to provide (at least) the secret, the cipher, the initialization vector, and the tag.
up
0
handsomedmm at 126 dot com
5 years ago
if encrypt data by openssl enc command with pass and salt, it can aslo decrypt by openssl_decrypt.

eg.

encrypt command:

# echo -n test123 | openssl enc -aes-128-cbc -pass pass:"pass123" -a -md md5

decrypt command:
# echo -n U2FsdGVkX19349P4LpeP5Sbi4lpCx6lLwFQ2t9xs2AQ= | base64 -d| openssl  enc -aes-128-cbc -pass pass:"pass123" -md md5 -d -p
salt=77E3D3F82E978FE5
key=9CA70521F78B9909BF73BAE9233D6258
iv =04BCCB509EC9E6F5AF7E822CA58EA557
test123

use php code
<?php
       
// encode data
       
$encodeData = "U2FsdGVkX19349P4LpeP5Sbi4lpCx6lLwFQ2t9xs2AQ=";

       
// base64 decode
       
$data = base64_decode($encodeData);
       
$data = substr($data, 16); // if salted , remove Salted__ prefix

        // salt and pass config
       
$salt   = hex2bin("77E3D3F82E978FE5");
       
$pass   = "pass123";
       
$method = "AES-128-CBC";

       
// generate iv and key
       
$hash1 = md5($pass . $salt);
       
$hash2 = md5(hex2bin($hash1) . $pass . $salt);
       
$key   = hex2bin($hash1);
       
$iv    = hex2bin($hash2);

       
$decodeData = openssl_decrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);

       
var_dump($decodeData);
up
0
public at grik dot net
14 years ago
The list of methods for this function can be obtained with openssl_get_cipher_methods();
The password can be encrypted with the openssl_private/public_encrypt()
up
-5
waltzie
5 years ago
There are some troubles implementing a 1:1 encryprion/decription between mcrypt and openssl using MCRYPT_RIJNDAEL_128 CBC because the AES-256 is different from RIJNDAEL-256.
The 256 in AES refers to the key size, where the 256 in RIJNDAEL refers to block size.
AES-256 is RIJNDAEL-128 when used with a 256 bit key
(https://stackoverflow.com/questions/6770370/aes-256-encryption-in-php  ircmaxell Jun 22 '13 at 11:50)

Example

<?php

function encrypt_openssl($msg, $key, $iv) {
       
$encryptedMessage = openssl_encrypt($msg, 'AES-256-CBC', $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING  , $iv);
        return
$iv . $encryptedMessage;
}

function
decrypt_openssl($data, $key) {
       
$iv_size = openssl_cipher_iv_length('AES-256-CBC');
       
$iv = substr($data, 0, $iv_size);
       
$data = substr($data, $iv_size);
        return
openssl_decrypt($data, 'AES-256-CBC', $key,OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING  , $iv);

}

function
decrypt_data($data,$key) {
       
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
       
$iv = substr($data, 0, $iv_size);
       
$data = substr($data, $iv_size);
       
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
       
$decrypted = rtrim($decrypted, chr(0));
        return(
$decrypted);
}

function
encrypt_data($data,$key,$iv) {
       
$encrypted = $iv mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
        return
$encrypted;
}

// ZERO Padding ISO/IEC 9797-1, ISO/IEC 10118-1
function pad_zero($data) {
       
$len = mcrypt_get_block_size (MCRYPT_RIJNDAEL_128,MCRYPT_MODE_CBC);
        if (
strlen($data) % $len) {
             
$padLength $len - strlen($data) %  $len;
             
$data .= str_repeat("\0", $padLength);
        }
        return
$data;
}

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$data = "Hello World!";
$key = hash('sha256',"secret",true);

echo
"\n\n$data\n\n";

$enc = base64_encode(encrypt_data($data,$key,$iv));
echo
"\nEnc: $enc";
$dec = decrypt_data(base64_decode($enc),$key);
echo
"\nDec: $dec";
$dec2=decrypt_openssl(base64_decode($enc),$key);
echo
"\nDec: $dec2";

echo
"\n\nreverse\n";

$enc2 = base64_encode(encrypt_openssl(pad_zero($data),$key,$iv));
echo
"\nEnc: $enc2";
$dec = decrypt_data(base64_decode($enc2),$key);
echo
"\nDec: $dec";
$dec2=decrypt_openssl(base64_decode($enc2),$key);
echo
"\nDec: $dec2";
up
-6
David
9 years ago
Note, that if you don't specify the ...RAW_DATA  option, then you get a base64 encoded result.  I lost a few hours because my PHP didn't have the OPENSSL_RAW_DATA constant, and after I'd carefully base64 encoded the result, it just wasn't decoding...
up
-15
Kukulkan
12 years ago
PHP OpenSSL functions openssl_encrypt() and openssl_decrypt() seem to use PKCS5/7 style padding for all symmetric ciphers. Upon this, you can't use them to encrypt using null byte padding or to decrypt null byte padded data.

The developers of the wrapper forgot the padding scheme flags... :(
up
-7
me at bobste dot in
7 years ago
<?php
string openssl_encrypt
(
   
string $data ,
   
string $method ,
   
string $key,
   
int $options = 0,      // optional
   
string $iv = "",       // optional
   
string &$tag = NULL,   // optional
   
string $aad = "",      // optional
   
int $tag_length = 16   // optional
)
// (a kinder, gentler function header)
?>
up
-16
kazaaknet at yahoo dot com
12 years ago
Be advised there was a memory leak in this function: https://bugs.php.net/bug.php?id=54060.  I believe this got fixed in 5.3.6, but on production webservers running 5.3.5 with modest traffic, this became  a memory hemorrhage that brought my site down.  Look at mcrypt_encrypt instead.
up
-14
Geoff
8 years ago
Contrary to some of the other comments here, I'm not certain that Password is indeed being improperly treated as the direct key.  I say this because I've been passing random text values into this parameter which would be invalid as hex input.  It seems to be hashing the password I provide, using what algorithm I do not know, because otherwise I'd expect it to throw an exception instead of working as expected.

That said, I'm using openssl_decrypt() to decrypt data that was only encrypted with openssl_encrypt().  I've not had to try to decrypt data where I do know for certain what the direct key is to know if I have an issue with bad pad blocks or any other exceptions which would indicate a key mismatch.
To Top