blinking_eyes blinking_eyes - 5 months ago 48
PHP Question

How to get an openssl_decrypt binary output?

I'm am trying to encrypt/decrypt some data using openssl_encrypt/openssl_decrypt. The purpose is as follows: the user enters some data in the GUI, this data is encrypted and stored at the database. Later it will be retrieved and decrypted only for a certain user profile.

The encrypt part is working fine since I've followed a colleague's advice, that is wrapping the output into a bin2hex function.

The whole point is I don't seem able to get a binary output when decrypting the data, even if I try to convert the output using hex2bin. I always get outputs like "� �Ps�1�_G�5�OUT".

I'm running out of ideas, actuallly I really don't know what to do at this point.

This is a sample code I've wrote to test this functions:

PHP

function encrypt($string) {

$cypher = 'aes-256-cbc';
$key = 's7aBkf4Ypn59bWviQziPDXyPasdaYlhQ';

$ivsize = openssl_cipher_iv_length($cypher);
$iv = openssl_random_pseudo_bytes($ivsize);

$encripted = openssl_encrypt(
$string, $cypher, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv
);

return $iv . $encripted;
}

function decrypt($string) {

$cypher = 'aes-256-cbc';
$key = 's7aBkf4Ypn59bWviQziPDXyPasdaYlhQ';

$ivsize = openssl_cipher_iv_length($cypher);
$iv = mb_substr($string, 0, $ivsize, '8bit');
$decrypted = mb_substr($string, $ivsize, null, '8bit');

$output = openssl_decrypt(
$decrypted, $cifrado, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv
);

return $output;
}


HTML:

<!doctype html>
<html>
<head><title>TEST</title></head>
<body>
<div style='margin-left:250px;'>
<form action="test.php" method="POST">
Encrypt
<input type="text" name="encrypt" value=''/>
<input type="submit" name="send" value='Send'/>
<br/><br/>
<?php
if (isset($_POST['encrypt']) && !empty($_POST['encrypt'])) {
echo 'encrypted string: ' . bin2hex(encrypt($_POST['encrypt']));
}
?>
<br/>
<br/>
Decrypt
<input type="text" name="decrypt" value=''/>
<input type="submit" name="send" value='Send'/>
<br/><br/>
<?php
if (isset($_POST['decrypt']) && !empty($_POST['decrypt'])) {
echo 'decrypted string: ' . decrypt($_POST['decrypt']);
var_dump(decrypt($_POST['decrypt']));
}
?>
</form>
</body>




Any ideas or help would be appreciated.

My PHP version is 5.4.45-0+deb7u2.

Thanks in advance.

Answer

Updated in line with the remarks by @Zaph.

There was a minor syntax error. However the issue with the code is that, in the encrypt routine you are only allowed to us one of: OPENSSL_RAW_DATA or OPENSSL_ZERO_PADDING. It is mentioned in the documentation.

Comment from @Zaph: PKCS#7 padding which it the general padding, should be used. It turns out that the default for PHP OpenSSL is PKCS#7, do not add any padding option and you will get the right thing.

Openssl_encrypt() adds PKCS7 padding to the plaintext before encrypting with a block cipher in CBC or ECB mode. Openssl_decrypt() strips the padding after decryption.

I am using PHP 5.4.4.

So, the only option needed was: OPENSSL_RAW_DATA.

To enable the encrypted string to be stored safely anywhere. I have base64_encoded it.

Demonstration at: ideone.com

Encryption:

/**
 * Encrypt a string
 * 
 *     
 * @Param string $data 
 * @Param string $key
 * 
 * @return string  - base64_encoded   
 */ 
function encrypt($data, $key) {

  $cypher = $cypher = 'aes-256-cbc';  
  $ivSize  = openssl_cipher_iv_length($cypher);
  $ivData  = openssl_random_pseudo_bytes($ivSize);

  $encripted = openssl_encrypt($data, 
                              $cypher, 
                              $key, 
                              OPENSSL_RAW_DATA, 
                              $ivData);


  return base64_encode($ivData  . $encripted);
}

Decryption:

/**
 * Decrypt a string
 * 
 * @Param string $data 
 * @Param string $key
 * 
 * @return string  - original text   
 */ 
function decrypt($data, $key) {

  $cypher = 'aes-256-cbc';  
  $ivSize  = openssl_cipher_iv_length($cypher);

  $data = base64_decode($data);
  $ivData   = substr($data, 0, $ivSize);

  $encData = substr($data, $ivSize);

  $output = openssl_decrypt($encData, 
                            $cypher, 
                            $key, 
                            OPENSSL_RAW_DATA, 
                            $ivData);
  return $output;
}

run it:

$srcText = "Hello World! - " . uniqid();
$key    = 's7aBkf4Ypn59bWviQziPDXyPasdaYlhQ';

$srcEncrypted  = '';
$srcDecrypted  = '';

$srcEncrypted = encrypt($srcText, $key);

$srcDecrypted = decrypt($srcEncrypted, $key);

var_dump($srcText, 
         $srcEncrypted, 
         $srcDecrypted, 
         $srcDecrypted == $srcText);

Sample output:

string 'Hello World! - 5776adf944c52' (length=28)

string 'NOjIIMM0visDbJPmBsAMgH+OQbYiReLBSvzg5JVZTMUOCAtk3CO7FBNs/Dn/vE9s' (length=64)

string 'Hello World! - 5776adf944c52' (length=28)

boolean true