3

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.

4
  • 1
    "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" - The best I can tell, the non-printables means you have binary output.
    – jww
    Commented Jun 30, 2016 at 13:23
  • @jww I've tried bin2hex on the decrypt output but nothing's changed. I need an output that humans can read, but don't seem to find the solution. :( Commented Jun 30, 2016 at 13:29
  • OpenSSL has OPENSSL_buf2hexstr, but I don't know if pyOpenSSL provides it. I was recently talking to one of the devs about a b16 or hex encoder for use with BIOs. It makes sense to me because its a recurring request, and its a perfect fit for I/O consistency. If the library provided Base64 encoders and decoders, then it can provide Base32 and Base16 encoders and decoders, too.
    – jww
    Commented Jun 30, 2016 at 13:33
  • The 'multibyte' string processing is is not required in the encryption routines as they just see the data as blocks of bytes. Commented Jul 2, 2016 at 8:28

1 Answer 1

4

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
1
  • @blinking_eyes, Glad it helped. Please accept it as an answer if you found it useful. :) Any issues then just post a comment :) Commented Jul 4, 2016 at 20:36

Not the answer you're looking for? Browse other questions tagged or ask your own question.