blinking_eyes
blinking_eyes

Reputation: 53

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.

Upvotes: 3

Views: 3425

Answers (1)

Ryan Vincent
Ryan Vincent

Reputation: 4513

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.

Comments 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. So, 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.

Please note that the IV (Initialisation Vector) is changed every time and stored in the output string. Why? If you provide the same string and password it is certain to be a different output string every time.

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 = '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

Upvotes: 4

Related Questions