intermittent openssl_decrypt error (IV of precisely 16 bytes) - php

I am receiving the following error when on decrypting certain data.
openssl_decrypt(): IV passed is only 12 bytes long, cipher expects an IV of precisely 16 bytes
I am also seeing the warning: hash_equals(): Expected known_string to be a string, boolean given.. perhaps some corrupted data.
I recently decrypted from mcrypt and re-encrypted with openssl (this code). I see I should be passing OPENSSL_RAW_DATA
here is my class:
class AES{
function __construct() {
$this->aes_256_cbc = 'aes-256-cbc';
}
function __destruct() {}
function encrypt($data,$passphrase){
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->aes_256_cbc));
$dataEncrRaw = openssl_encrypt($data, $this->aes_256_cbc, $passphrase, 0, $iv);
$hmac = hash_hmac('sha256', $dataEncrRaw, $passphrase, $as_binary=true);
return base64_encode( $iv.$hmac.$dataEncrRaw );
}
function decrypt($dataEncr,$passphrase){
$data = '';
$dataEncr = base64_decode($dataEncr);
$ivlen = openssl_cipher_iv_length($cipher=$this->aes_256_cbc);
$iv=substr($dataEncr,0,$ivlen);
$hmac = substr($dataEncr, $ivlen, $sha2len=32);
$dataEncrRaw=substr($dataEncr,$ivlen+$sha2len);
$dataPlainText = openssl_decrypt($dataEncrRaw, $this->aes_256_cbc, $passphrase, 0, $iv);
$calcmac = hash_hmac('sha256', $dataEncrRaw, $passphrase, $as_binary=true);
if (hash_equals($hmac, $calcmac)){
$data=$dataPlainText;
}
return $data;
}
}
Thanks,
Drew

Related

Open SSL Encrypt PHP

I'm trying to use PHP's openssl_encrypt() function but my key is coded in hexadecimal and the function is returning an error. When using the hex2bin() function to convert the key to binary, the return value is garbled ASCII text. Then when inserted into openssl_encrypt(). I get an error.
define('TEX_ENCRYPTION_KEY', 'hexadecimalkey...');
define('TEX_ENCRYPTION_IV', 'hexadecimalkey...');
$key = hex2bin(TEX_ENCRYPTION_KEY);
$iv = hex2bin(TEX_ENCRYPTION_IV);
$transData = '<Detail>blah blah blah</Detail>';
$alg = 'aes-256-cbc';
$encryptedData = openssl_encrypt(
$transData,
$alg,
$key,
OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING,$iv
);
This outputs an error:
error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data
not multiple of block length
Any idea what is going on here?
Though it's not in the official documentation, there's a pretty good explanation of what the OPENSSL_ZERO_PADDING option does in the comments. By default, OpenSSL will pad your plaintext to a multiple of the cipher block size (16 bytes in the case of AES-256-CBC.) However, you've disabled that mechanism and OpenSSL is expecting you to ensure the length of your data is a multiple of 16. It's not, so you get the error message "data not multiple of block length."
Solution: pad your data or remove that option!
<?php
$transData = '<Detail>blah blah blah</Detail>';
$transData = str_pad(
$transData,
strlen($transData) + (16 - (strlen($transData) % 16)),
chr(0)
);
After dancing with the openssl documentation I had the solution to replace depreciated Mcrypt function with openssl (openssl_encrypt and openssl_decrypt functions) and return ASCII text with base64_encode():
//Return encrypted string
public function stringEncrypt ($plainText, $cryptKey = '7R7zX2Urc7qvjhkr') {
$length = 8;
$cstrong = true;
$cipher = 'aes-128-cbc';
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt(
$plainText, $cipher, $cryptKey, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $cryptKey, $as_binary=true);
$encodedText = base64_encode( $iv.$hmac.$ciphertext_raw );
}
return $encodedText;
}
//Return decrypted string
public function stringDecrypt ($encodedText, $cryptKey = '7R7zX2Urc7qvjhkr') {
$c = base64_decode($encodedText);
$cipher = 'aes-128-cbc';
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ivlenSha2len = $ivlen+$sha2len;
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$plainText = openssl_decrypt(
$ciphertext_raw, $cipher, $cryptKey, $options=OPENSSL_RAW_DATA, $iv);
}
return $plainText;
}

Why does my variable change length in this example?

I am trying to encrypt and decrypt a value. My example sometimes work but most of the time I am getting:
openssl_decrypt(): IV passed is 20 bytes long which is longer than the
16 expected by selected cipher, truncating
These are my 2 methods:
Encrypt
public function encrypt(string $value)
{
$iv = random_bytes(16);
$encrypted = openssl_encrypt($value, $this->cipher, $this->key, 0, $iv);
return base64_encode($encrypted . '/' . $iv);
}
Decrypt
public function decrypt(string $payload)
{
$payload = base64_decode($payload);
list($payload, $iv) = explode('/', $payload);
$decrypted = openssl_decrypt($payload, $this->cipher, $this->key, 0, $iv);
return $decrypted;
}
The length of my $iv variable has changed after I decrypt it. Could anybody tell me why this happens? Whenever I var dump the random bytes output I sometimes get weird characters as well that can not be rendered.
Could anybody point my in the right direction so I can fix this?
Cheers.

Replacing mcrypt_encrypt using MCRYPT_RIJNDAEL_256 with openssl_encrypt

As you guys probably know, the extension mcrypt will be deprecated on php 7.1.
I use to maintain a "legacy" application that I want to migrate eventually to this version so I ran the tests and verified that I can't get 100% of coverage anymore, since there's a piece of code that use the following code:
$key = 'sA*(DH';
// initialization vector
$iv = md5(md5($key));
$output = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $string, MCRYPT_MODE_CBC, $iv));
I tried to port this piece of code to openssl_encrypt using this code
$key = md5('sA*(DH');
$iv = md5($key);
echo base64_encode(openssl_encrypt($data, "aes-256-cbc", $key, OPENSSL_RAW_DATA, $iv));
But I have 2 problems with this:
The IV lenght should be 16 chars (and md5 gives me 32), so I get a PHP Warning
The output it's not the same (even if I truncate to 16 chars)
Anyone had similar problems (or know how to fix it?)
BTW: I'm using the dev master version of PHP (supposed to be 7.1.0 alpha 3).
Yet another tested solution taking and returning ANSI text to replace Mcrypt function with the openssl_encrypt() and openssl_decrypt():
//Return encrypted string
public function stringEncrypt ($plainText, $cryptKey = '7R7zX2Urc7qvjhkr') {
$cipher = 'aes-128-cbc';
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt(
$plainText, $cipher, $cryptKey, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $cryptKey, $as_binary=true);
$encodedText = base64_encode( $iv.$hmac.$ciphertext_raw );
}
return $encodedText;
}
//Return decrypted string
public function stringDecrypt ($encodedText, $cryptKey = '7R7zX2Urc7qvjhkr') {
$c = base64_decode($encodedText);
$cipher = 'aes-128-cbc';
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ivlenSha2len = $ivlen+$sha2len;
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$plainText = openssl_decrypt(
$ciphertext_raw, $cipher, $cryptKey, $options=OPENSSL_RAW_DATA, $iv);
}
return $plainText;
}
More read in openssl documentation
You should really get out of the habit of using md5 for anything.
$iv = openssl_random_pseudo_bytes(16);
$key = substr(hash('sha256', 'sA*(DH'), 0, 32)
mcrypt_encrypt and openssl_encrypt will not output the same crypttext given the same plaintext and key.
also, mcrypt is deprecated in PHP 7.1, not removed...so you can update to 7.1 without changing from mcrypt to openssl ... but it is a good idea to remove mcrypt in general.
There are 2 problems :
MCrypt uses zero padding while Openssl uses by default PKCS#7
Openssl needs the input string to be of proper length (multiple of block length)
To solve this problems :
add OPENSSL_ZERO_PADDING flag to openssl_encrypt/openssl_decrypt
if input string length is not multiple of block length then append to the input string zero chars "\0" [aka chr(0)];
That being said this should solve the problem:
// key/iv in ASCII binary data, $str base64
function decrypt_stuff($key, $str, $iv) {
// $plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($str), MCRYPT_MODE_CBC, $iv);
$plaintext_dec = openssl_decrypt(base64_decode($str), "aes-256-cbc", $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
return $plaintext_dec;
}
// key/iv in ascii binary data, $str ascii
function encrypt_stuff($key, $str, $iv) {
// $ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_CBC, $iv));
if (($l = (strlen($str) & 15)) > 0) { $str .= str_repeat(chr(0), 16 - $l); }
$ciphertext = base64_encode(openssl_encrypt($str, "aes-256-cbc", $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv));
return $ciphertext;
}

getting Scrambled text using mcrypt for securing url data in php

m using
public function encrypt($plain_str,$key)
{
$str= mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plain_str, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND));
$str = urlencode(base64_encode($str));
return $str ;
}
public function decrypt($cipher_str,$key)
{
$str = urldecode(base64_decode($cipher_str));
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND));
}
on crypting :201433~58~g#fds.com~20140820142427
i get : %2BAihYMLwpwrsmL4lSGGzwFTfonvdCyOb%2BCGEUJ%2F%2BE%2F7ZnvgwFRYFtlazQeSrVjUjyaaGZADK8%2BZyynIGxyt4VQ%3D%3D
on decrypting : %2BAihYMLwpwrsmL4lSGGzwFTfonvdCyOb%2BCGEUJ%2F%2BE%2F7ZnvgwFRYFtlazQeSrVjUjyaaGZADK8%2BZyynIGxyt4VQ%3D%3D
i get :201433~58~g#fds.com~20140820142427 back but
when string is malformed like some character removed
like this : %2BAihYMLwpwrsmL4lSGGzwFTfonvdCyOb%2BCGEUJ%2F%2BE%2F7Z
on decrypting i get : 201433~58~g#fds.com~201408201424O#¿W«Gݽˋ¯ È#'oP´ŸØw\Â⦑
How can i detect this anomoly ?
First of all, I'd like to list some flaws in your code:
Don't use ECB mode.
You are encrypting using MCRYPT_RIJNDAEL_128, but you're getting the IV size for MCRYPT_RIJNDAEL_256. (btw, IV is ignored in ECB mode, which is one of the reasons why not to use it)
You are also using MCRYPT_RAND as your randomness source, which is not secure. You should use MCRYPT_DEV_URANDOM (that is also the new default in PHP 5.6).
You don't have to urlencode() the resulting ciphertext, Base64 encoding is URL-safe.
Now, to answer your question ... this is done via a HMAC. The easiest way to use a HMAC is to prepend the cipher-text with it (which you should do with the IV as well; don't worry, it's not a secret):
public function encrypt($plainText, $encKey, $hmacKey)
{
$ivSize = mcrypt_get_iv_size('rijndael-128', 'ctr');
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$cipherText = mcrypt_encrypt('rijndael-128', $encKey, $plainText, 'ctr', $iv);
$cipherText = $iv.$cipherText;
$hmac = hash_hmac('sha256', $cipherText, $hmacKey, true);
return base64_encode($hmac.$cipherText);
}
public function decrypt($cipherText, $encKey, $hmacKey)
{
$cipherText = base64_decode($cipherText);
if (strlen($cipherText) <= 32)
{
throw new Exception('Authentication failed!');
}
$recvHmac = substr($cipherText, 0, 32);
$cipherText = substr($cipherText, 32);
$calcHmac = hash_hmac('sha256', $cipherText, $hmacKey, true);
if ( ! hash_equals($recvHmac, $calcHmac))
{
throw new Exception('Authentication failed!');
}
$ivSize = mcrypt_get_iv_size('rijndael-128', 'ctr');
$iv = substr($cipherText, $ivSize);
$cipherText = substr($cipherText, $ivSize);
return mcrypt_decrypt('rijndael-128', $encKey, $cipherText, 'ctr', $iv);
}
Please note that the encryption key and HMAC key are different - they most NOT be the same key. Also, for Rijndael-128, you should create a 128-bit (or 16-byte) random key, it is not something that you can just type in with your keyboard. Here's how to generate one:
$encKey = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);

issues trying to get openssl_encrypt to work

I'm writing a class to handle encrypted data, essentially it will be used to encrypt data to be stored in a DB and then again to decrypt it on retrieval.
Here's what I've written:
class dataEncrypt {
private $encryptString;
private $decryptString;
private $encryptionMethod;
private $key;
public function __construct() {
/* IMPORTANT - DONT CHANGE OR DATA WILL DAMAGE */
$this->key = sha1('StringToHash');
// Set the encryption type
$this->encryptionMethod = "AES-256-CBC";
}
// Generate the IV key
private function generateIV() {
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
return mcrypt_create_iv($ivSize, MCRYPT_RAND);
}
// Retrieve the key
private function retrieveKey() {
return $key;
}
// Encrypt a string
public function encryptString($string) {
// Return the encrypted value for storage
return openssl_encrypt($string, $this->encryptionMethod, $this->retrieveKey(), 0, $this->generateIV());
}
// Decrypt a string
public function decryptString($data) {
// return the decrypted data
return openssl_decrypt($data, $this->encryptionMethod, $this->retrieveKey(), 0, $this->generateIV());
return false;
}
}
I'm trying to encrypt a string before storing, and I get the following PHP warning:
Warning: openssl_encrypt(): IV passed is 32 bytes long which is longer than the 16 expected by selected cipher, truncating in /var/www/blahblah... on line xxx
I've googled this, Ive googled the IV functions, I can't find sweetheat on either. Any advice is welcomed here.
Thanks
I was able to get it working by passing MCRYPT_CAST_256 rather than MCRYPT_RIJNDAEL_256 into mcrypt_get_iv_size
Encrypt:
$iv_size = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$encrypted = openssl_encrypt($string, "AES-256-CBC", $key, 0, $iv);
$encrypted = $iv.$encrypted;
Decrypt
$iv_size = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CBC);
$iv = substr($string, 0, $iv_size);
$decrypted = openssl_decrypt(substr($string, $iv_size), "AES-256-CBC", $key, 0, $iv);

Categories