I have a string encrypted with AES in Counter mode, it wasn't done with PHP and I can't make mcrypt to decode it :( there's a class that works as expected: http://www.movable-type.co.uk/scripts/aes.html (see bottom of the page), but it's slow so I want to decrypt with mcrypt.
According to classes decrypt method I did following:
$key = $_POST['key'];
$length = strlen($key);
if($length > 32)
$key = substr($key, 0, 32);
$cyphered = base64_decode($_POST['cyphered']);
/// make initialization vector with first 8 bytes treated as integers
$f8b = array_map('ord', str_split(substr($cyphered, 0, 8)));
array_unshift($f8b, 'I8');
$iv = call_user_func_array('pack', $f8b);
print mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, substr($cyphered, 8), 'ctr', $iv);
And result is garbage on output :( I don't understand what am I doing wrong. Could somebody please sched some light on this problem?
phpseclib's pure PHP AES implementation is almost 4x as fast as movable type's AES implementation:
http://phpseclib.sourceforge.net/crypt/examples.html
And that's when mcrypt isn't available. When mcrypt is available it'll use that and be even faster.
That said, if you are insistent on using mcrypt... try MCRYPT_RIJNDAEL_128. MCRYPT_RIJNDAEL_256 has a block size of 256 bits, which works for Rijndael (Rijndael has a variable block size) but not AES.
Related
I have an old piece of code that's written using mcrypt extension and I have to change it to phpseclib. But my code doesn't generate the same output as mcrypt function:
Old code:
$encryptedText =mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$myKey,
$data ,
MCRYPT_MODE_CBC,
$myIV
);
My new code:
$aes = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_CBC);
$aes->setKey($myKey);
$aes->setIV($myIV);
$aes->disablePadding();
$seclib = $aes->encrypt( $data );
but $encryptedText and $seclib are not the same.
You're equating Rijndael with AES, a common misconception.
AES is only a subset of it - Rijndael-128, with the difference between AES variations being only the key size:
AES-128 is Rijndael-128 with a 128-bit key.
AES-256 is again Rijndael-128, but with a 256-bit key.
The suffix number in Rijndael variations on the other hand, refers to both key size and block size, so of course you cannot get Rijndael-256 by doing AES, as you need a 256-bit block size.
There's a page on the phpseclib docs, which generates sample code after you input the basic variables (cipher, mode, key size, bit size). It outputs the following for Rijndael, CBC, 256, 256:
<?php
include('Crypt/Rijndael.php');
include('Crypt/Random.php');
$cipher = new Crypt_Rijndael(); // could use CRYPT_RIJNDAEL_MODE_CBC
$cipher->setBlockLength(256);
// keys are null-padded to the closest valid size
// longer than the longest key and it's truncated
//$cipher->setKeyLength(256);
$cipher->setKey('abcdefghijklmnopqrstuvwxyz123456');
// the IV defaults to all-NULLs if not explicitly defined
$cipher->setIV(crypt_random_string($cipher->getBlockLength() >> 3));
$size = 10 * 1024;
$plaintext = str_repeat('a', $size);
echo $cipher->decrypt($cipher->encrypt($plaintext));
I am not sure if the library actually supports this cipher without mcrypt availablity, but it should.
I assume you are doing this because mcrypt is being dropped from PHP, and I strongly suggest that you change your strategy.
Even if the above works, it would be quite slow when using a userland PHP implementation of the algorithm (something which is noted in the phpseclib docs), but more importantly - you'll have no other alternatives if this library stops working, is no longer maintained, etc. Non-AES variations of Rijndael are not ubiquitous, and there are more modern algorithms available today anyway (hint: libsodium being added to PHP 7.2).
If I were you, I'd change the algorithm entirely. Of course, that would mean re-encrypting all of the data, but you'll have to do that eventually and now is really the best time to do it.
I am working on a project that requires the AES encryption of a soap envelope using the requirements below.
Encryption key: myKey-1234567abcdef
AES-256 encryption
128 block size
PKCS7 padding
16 bit vector (vector is attached before encrypted message)
Cipher Block Chaining (CBC)
This is what I tried:
$key = 'myKey-1234567abcdef';
$encryptionMethod = "AES-256-CBC";
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = substr(mcrypt_create_iv($iv_size, MCRYPT_RAND), 0, 16);
$xml = openssl_encrypt($xml,$encryptionMethod, $key, 0, $iv);
I am currently getting a 400 bad request error and there seems to be lots of options for argument constants mcrypt functions, was wondering if my implementation satisfies the needs for padding, block size and vector?
I appreciate any suggestions, thanks in advance!
First: Your key isn't an appropriate length for AES-256. I realize the key you're using here is an example, but make sure that the one you've been provided is 32 characters (256 bits) long. If it isn't, ask the recipient for clarification.
Second: You're mixing the mcrypt and openssl extensions inappropriately here. You shouldn't be using mcrypt anyway, as it's unmaintained, sometimes broken, and will be removed entirely in PHP 7.2. Instead, hard-code the IV size as 16 and use openssl_random_pseudo_bytes to generate it:
$iv = openssl_random_pseudo_bytes(16);
$xml = openssl_encrypt($xml, $encryptionMethod, $key, 0, $iv);
Third: By default, openssl_encrypt() encodes its output as Base64. The recipient may not be expecting this. Pass OPENSSL_RAW_DATA as the fourth argument to openssl_encrypt() (replacing 0) to get unencoded output.
I got the following requirements for the encryption for the API i am currently trying to access:
PKCS7 padding method
CBC encryption mode
AES key size 256, block size 128
Everytime i submit to the API with the encryption, there seems to be something wrong with the API (unfortunately no error is produced).
$Data = "GOOD!";
$aesKey = "1234567812345678";
$EncryptedData = encrypt($aesKey,$Data);
$DecryptedData = decrypt($aesKey,$EncryptedData);
echo "Orignal Data : ". $Data;
echo "<br/>";
echo "After encryption = ". $EncryptedData;
echo "<br/>";
echo "After decryption = " .$DecryptedData;
function encrypt($aesKey, $dataToEncrypt) {
$output = false;
$iv = '{{{{{{{{{{{{{{{{';
$output = openssl_encrypt($dataToEncrypt, 'AES-128-CBC', $aesKey,
OPENSSL_RAW_DATA, $iv);
$output = base64_encode($output);
return $output;
}
function decrypt($aesKey, $dataTodecrypt) {
$output = false;
$iv = '{{{{{{{{{{{{{{{{';
$dataTodecrypt = base64_decode ($dataTodecrypt);
$dataTodecrypt = $output = openssl_decrypt($dataTodecrypt, 'AES-128-CBC',
$aesKey, OPENSSL_RAW_DATA, $iv);
return $output;
}
Questions:
What exactly is PKCS7 padding method and can be implemented with php?
AES 256 is fine but what exactly does block size mean?
What exactly does IV do?
AES 256 is fine but what exactly does block size mean?
AES has a fixed block size of 128 bit. A block cipher only works on one block of a specific size. A mode operation extends a block cipher with the ability to work on multiple blocks and a padding enables it to work on plaintexts that are not a multiple of the block size.
AES-128-CBC means AES with key size of 128 bit and the CBC mode of operation. If you want to use AES-256, then you need to tell OpenSSL that: AES-256-CBC. Additionally, you need to use a key that is actually 256 bit long. Your current key is only 128 bit long.
What exactly is PKCS7 padding method and can be implemented with php?
openssl_encrypt() already does PKCS#7 padding for you and openssl_decrypt() removes it for you.
What exactly does IV do?
A random IV randomizes the ciphertext which means that encrypting the same plaintext with the same key, but a different IV produces a different ciphertext which is indistinguishable from random noise or other the same encryption with a different IV. Wikipedia has a good description what this actually does.
Keep in mind that an IV must be randomly generated for each iteration. Otherwise, an attacker who observes only the ciphertext may discover that you encrypted the same plaintext multiple times.
Keep in mind that an AES key is supposed to be quite noisy with high entropy. "12345..." looks more like a password. If you want to use passwords, then you need to derive a key from that password. PBKDF2 is a good idea with a random salt and a lot of iterations.
What exactly is PKCS7 padding method and can be implemented with php?
I am not really certain 'padding' is the phrase you mean here. While the PKCS#7 format does rely on padding, the example you provide has absolutely nothing to do with asymmetric encryption and the ASN.1 format for the PKCS#7 messaging syntax mentioned.
AES 256 is fine but what exactly does block size mean?
Block size is the bit size an encryption cipher, like AES-256, operates on per permutation.
What exactly does IV do?
An IV is short for initialization vector or for some symmetric encryption cipher implementations it can also be referred to as an nonce.
Both are used to help strengthen the resulting cipher text. You can think of them as being similar to a salt for a non-reversible hashing algorithm.
You should avoid re-using the same IV.
In regards to your example; the documentation for openssl_encrypt() states the following function usage:
string openssl_encrypt ( string $data , string $method , string $password [, int $options = 0 [, string $iv = "" ]] )
Your encryption of the plain text looks accurate (while I would choose AES-256-GCM vs. AES-128-CBC as the algorithm, blocksize & mode):
$output = openssl_encrypt($dataToEncrypt, 'AES-128-CBC', $aesKey,
OPENSSL_RAW_DATA, $iv);
Without testing I am assuming you are getting a valid base64 encoded value.
The manual for the openssl_decrypt() method states the following usage:
string openssl_decrypt ( string $data , string $method , string $password [, int $options = 0 [, string $iv = "" ]] )
While technically your implementation is correct; I would suggest the following (note the double assignment to $dataTodecrypt = $output = openssl_decrypt()):
$output = openssl_decrypt(base64_decode($dataTodecrypt), 'AES-128-CBC', $aesKey, OPENSSL_RAW_DATA, $iv);
Since mcrypt is considered obsolete, my task is upgrading the current code to use openssl. Sounds simple, but ... after a few days of try and failure I feel like going insane.
My question to you is: Is there any way you can decrypt with openssl data previously encrypted with mcrypt? I've read so many posts on this matter and most of them say that a previous manual padding of the data was/is necessary before running mcrypt on it.
The issue is that the mcrypt-ed data is already encrypted (with the automatic null padding mcrypt provides) and resides in a database, so modification of that is not possible and/or desired.
Mentions:
the algorithm used is rijndael-128 cbc with a 32-byte key (so I'm using aes-256-cbc for openssl).
I'm using an openssl wrapper for php (php-crypto).
I've managed to make the inverse operation work (decode openssl with mcrypt) by simply stripping the end decoded characters if they were non alpha-numerical.
Manually padding the data before mcrypt-ing and then decrypting it using openssl works like a charm, but that's not the problem here.
Some code snippets:
// Simple mcrypt encrypt, decrypt with php-crypto example
// This doesn't work and produces a "Finalizing of cipher failed" error
$data = "This is a text";
$strMcryptData=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
$algorithm = 'aes-256-cbc';
$cipher = new Cipher($algorithm);
$sim_text = $cipher->decrypt($strMcryptData, $key, $iv);
// Simple mcrypt encrypt with padding, decrypt with php-crypto
// Works and produces the correct text on decryption
$pad = $blocksize - (strlen($data) % $blocksize);
$text = $data;
$text .= str_repeat(chr($pad), $pad);
$strPaddedData=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
$sim_text = $cipher->decrypt($strPaddedData, $key, $iv);
Slightly old, but you can solve this with a bit of work. You can tell PHP's OpenSSL that the encrypted string is not padded, and tell it to give you the raw output (So you don't have to base64 decode it, either). You can then strip nulls from the end of the resulting string if the length of the string happens to be perfectly divisible by the IV (This is a sanity check, as if the resulting string isn't divisible by the IV then it wasn't padded at all).
Be aware, this code has two major limitations:
If, at any point, you encrypted a legitimate string that ended in two or more NULL bytes then this code will not give you the same output.
If the padding of the string needed only one null byte, then this code won't strip it.
You can solve both of these if you know for a FACT that you didn't encrypt anything that ends in null bytes, you can alter the code that strips the nulls to just do a preg_replace; just make sure you anchor the regex to the end of the string so it only strips from the end.
<?php
$message = 'test';
$key = openssl_random_pseudo_bytes(16);
$iv = openssl_random_pseudo_bytes(16);
$cipher = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$message,
MCRYPT_MODE_CBC,
$iv
);
$plain = openssl_decrypt(
$cipher,
'aes-128-cbc',
$key,
OPENSSL_RAW_DATA | OPENSSL_NO_PADDING,
$iv
);
//try to detect null padding
if (mb_strlen($iv, '8bit') % mb_strlen($plain, '8bit') == 0) {
preg_match_all('#([\0]+)$#', $plain, $matches);
if (mb_strlen($matches[1][0], '8bit') > 1) {
$plain = rtrim($plain, "\0");
trigger_error('Detected and stripped null padding. Please double-check results!');
}
}
var_dump(
$message,
bin2hex($cipher),
$plain,
mb_strlen($message, '8bit'),
mb_strlen($plain, '8bit'),
$message === $plain
);
http://3v4l.org/kYAXn
Obviously this code comes with no major disclaimer and please test it in your use case, but someone might hopefully find this useful.
If you encrypt in mcrypt without adding PKCS7 manually, mcrypt will happily pad your plaintext with NUL bytes.
OpenSSL will do PKCS7 padding for you whenever using aes-X-cbc. The unfortunate consequence of this is that if you have AES-CBC(NULL_PADDED(plaintext)) and try to decrypt it, openssl_decrypt will attempt to remove the padding and fail.
Compare http://3v4l.org/bdQe9 vs http://3v4l.org/jr68f and http://3v4l.org/K6ZEU
The OpenSSL extension does not currently offer you a way to say "This string is not padded, please don't strip the padding for me" and then remove the NUL bytes on your own. You must encrypt with PKCS7 padding in order for decryption to succeed.
Although this is a limitation of OpenSSL, it bears emphasizing that the only reason you're running into it is because mcrypt is terrible.
There shouldn't be any major differences except for the padding. You should be able to call EVP_CIPHER_CTX_set_padding if you use the higher level OpenSSL (EVP) constructs directly. I presume that the padding argument should be zero, although it is not documented. You need a preconfigured encryption/decryption context for this.
Afterwards you will have your plaintext of the same length as the ciphertext. Zero to fifteen bytes at the end will be set to zero. You need to remove these bytes manually. If the plaintext happens to end with zero bytes then those will also be removed; that's however never the case if the plaintext is a printable string (that uses 8 bit encoding). You may want to ensure that you don't remove more than 15 bytes.
If you get completely random plaintext then your key or ciphertext is incorrect. If you get readable plaintext but for the first 16 bytes then your IV handling is incorrect.
I have been trying to encrypt a string in PHP and Ruby using the same key and iv but I always got different results.
Below is the PHP Code
$data = "This string needs to be encrypted";
$key = "1234567887654321abcdefghabcdefgh";
$iv = "1234567887654321abcdefghabcdefgh";
echo $encrypted_data = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv);
Below is the Ruby Code
data = "This string needs to be encrypted"
key = "1234567887654321abcdefghabcdefgh"
iv = "1234567887654321abcdefghabcdefgh"
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
aes.encrypt
aes.key = key
aes.iv = iv
encrypted_data = aes.update(data) + aes.final
Could somebody please help me get the same encrypted data in PHP and Ruby? I encrypted some data in PHP and then decrypted in Ruby but didn't get the data back. So I think the issue is PHP and Ruby encryption and decryption mechanism work differently. Please correct me if I am wrong. Thanks
Don't hard code IV's , it is insecure. IVs must be random but can be public , so just use
mcrypt_create_iv and prepend it to the front of the ciphtertext and then extract it before
decrypting
You likely have three problems
MCRYPT_RIJNDAEL_256 is nott AES. AES is a specific version RIJNDAEL that was standardized with a 128 bit block size and either 128 or 256 bit keys. MCRYPT_RIJNDAEL_256 is RIJNDAEL with a 256 bit block size. You want to use MCRYPT_RIJNDAEL_128 which is actually AES. For php, key length is just determined by the length of the key. So just give it a 256 bit ( 32 character) key and you will be fine. Note block size does not effect security really, so don't worry about the deference and just use AES with a 256 bit key: its good enough for the NSA and top secret data.
Padding. AES only takes fixed 128 bit chunks, so you must pad out the text to be a multiple of that size. PHP doesn't really bad and i believe SSL uses pkcs7 padding. Note that even with different padding schemes, for most of them the start of the cipher text should the the same there just may be garbage at the end.
String encoding. AES is defined with bit inputs, in c typically this is a byte array. Ruby and PHP use strings. I'd be willing to bet your string encodings are different.