PHP (mcrypt_encrypt) Ruby (AES-256-CBC) Encryption Different Results - php

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.

Related

About RC4 Decryption PHP (mcrypt)

I'm looking for anything about RC4 Decryption with decode the input using: Hexa
Lucky for me, I found
PHP's mcrypt_encrypt.
I want to decrypt many cipher files with the same key.
But, I had a problem with:
$iv_size = mcrypt_get_iv_size(MCRYPT_ARCFOUR, MCRYPT_MODE_STREAM);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
print (mcrypt_decrypt(MCRYPT_ARCFOUR, $key, $text, MCRYPT_MODE_STREAM, $iv));
(And UTF-8 Vietnamese)
The result of echo $iv_size is 0.
Please help me, I don't know how I can fix it?
Key : Lyr1cjust4nct (key file .txt)
Mode: STREAM
Decode the input using: Hexa
Ciphertext: cipher.txt (Hexa)
http://pastebin.com/bmYcmU0J
RC4 doesn't support IVs. You instead need to use a unique key for each message.
RC4 has two big weaknesses that apply to your situation:
Using related keys is not secure. So you can't just concatenate a fixed key with a variable/unique IV. You'd need to use some kind of hashing scheme.
The beginning of the output is very biased, which leaks information about the ciphertext. So you need to throw away the beginning of the key-stream. I think throwing away 1024 bytes should take care of the biggest biases.
RC4 doesn't include any integrity protection (MAC). So if an attacker manipulates the ciphertext, you'll run into problems.
=> Don't use RC4. Use AES in an authenticated mode such as GCM or by combining AES with a MAC using the encrypt-then-MAC principle.
I strongly recommend using a high level library written by experts, since people get encryption wrong very often, even when using standard primitives like AES.

AES 256 encryption PHP with Padding

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);

32-character PHP AES Key for mcrypt_encrypt

Consider the following PHP code:
<?php
$key = "1234567812345678";
$iv = "1234567812345678";
$data = "Test string";
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
$key,
$data,
MCRYPT_MODE_CBC,
$iv);
print "Encoded1: " . base64_encode($encrypted) . "\n";
$key = "12345678123456781234567812345678";
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
$key,
$data,
MCRYPT_MODE_CBC,
$iv);
print "Encoded2: " . base64_encode($encrypted) . "\n";
When run, this produces the output:
Encoded1: iz1qFlQJfs6Ycp+gcc2z4w==
Encoded2: n3D26h/m8CSH0CE+z6okkw==
Note that I stole the first bit of code from PHP Java AES CBC Encryption Different Results
Now - here's the question:
In the first case, the key that was passed in was a string of 16 characters. If each of the individual characters was interpreted as an 8-bit quantity, this gives the 128-bit key size that one would expect. Indeed, the Java code that's on the StackOverflow page that I referenced above does exactly that, and obtains the same result as the PHP.
In the second call to mcrypt_encrypt above, I have doubled the length of the key. mcrypt_encrypt accepts this happily, but produces a different encrypted output than in the first case. Clearly, therefore, it considers this a different key - it does not, for example take only the first 128 bits and discard any past that.
So, how does mcrypt_encrypt process the input key string to come up with the 128-bit key that the MCRYPT_RIJNDAEL_128 algorithm requires?
If it makes any difference, the case I'm specifically interested in is when a 32-character string is passed in like my second example - I have to create a matching decryption routine (in Java), so I need to figure out how the key is actually generated in this case. The page I cited has perfectly-good Java code (which works with all my test cases) - I'm just missing the proper set of key bytes.
There are two important parameters for the Rijndael algorithm. There is the key size (128-bit, 192-bit and 256-bit) and then there is the block size (128-bit, 192-bit and 256-bit). The 128 in MCRYPT_RIJNDAEL_128 refers to the block size. The key size is variable.
When you pass keys of different lengths into MCrypt, it will select the appropriate key size automatically, so you don't and can't set it. MCRYPT_RIJNDAEL_128 is AES (AES-128, AES-192, AES-256). MCRYPT_RIJNDAEL_192 and MCRYPT_RIJNDAEL_256 are not AES anymore.
If the Java code produced a matching result for the 128-bit key, then it will produce a matching result for the 256-bit key as well.
MCrypt is a little strange. Before PHP version 5.6.0 it would take any key length and not just 128-bit, 192-bit or 256-bit. The key would be filled up with 0x00 bytes up to the next valid key length.
Since Java doesn't support ZeroPadding out of the box, you should use a proper padding scheme such as PKCS#5/PKCS#7 padding in PHP. This answer has a very good implementation of it.

How can I decrypt data encrypted with 'rijndael-128-cbc' in ruby

I need to decrypt data which is encrypted by php with 'rijndael-128-cbc' algorithm.
Now I have a problem to convert php code to ruby code.
ruby OpenSSL::Cipher doesn't support rijndael-128-cbc then I use "aes-128-cbc".
I heard AES is based on the Rijndael cipher, so I guess I can convert decryption with rijndael-128-cbc' to AES-128-CBC.
enc = OpenSSL::Cipher.new "AES-128-CBC"
enc.encrypt
puts enc.key_len
It's output is 16
However, In php
echo mcrypt_get_key_size('rijndael-128', 'cbc')
this gets 32
Is there any difference between 'rijndael-128' and "AES-128-CBC"?
and How can I convert rijndael-128-cbc descrytion to ruby?
I am no Ruby developer, so I can't help you with the ruby code, but I can give you a few pointers about Rijndael, and PHP's mcrypt extension ...
Yes, AES is based on Rijndael-128 and therefore Rijndael-128 and AES-128 are the same thing.
It's important to note that the number in Rijndael's variations refers to the cipher's block size, while for AES - it's the key size, so (for example) Rijndael-192 is NOT AES-192; AES always has a block size of 16 bytes or 128 bits.
That being said, Rijndael-128 can also be AES-192 and AES-256, the difference being only the key size.
As described in the PHP manual for mcrypt_get_key_size(), the function returns the maximum key length for the supplied cipher ...
AES-256 == Rijndael-128 with a 32-byte key
This is why it's returning 32. For AES-128, the key size is of course 16 - you can hardcode that.
Hopefully, that will clear it up a bit for you.

Picking encryption cipher for mcrypt

I have few questions about this code:
<?php
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "This is a very secret key";
$text = file_get_contents('path/to/your/file');
echo strlen($text) . "\n";
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);
file_put_contents('path/to/your/file', $crypttext);
?>
It encrypts the file just fine, however it adds additional nulls at the end, so if I encrypt:
a test string is this one
and here is a new line
once decrypted becomes:
a test string is this one
and here is a new line 000000000000000
What's going on?
Second, is MCRYPT_RIJNDAEL_256 compatible with AES-128?
Finally, how would I let another party decrypt a file I've encrypted? They would need to know which encryption was used and I am not sure what to tell them.
Much of what I'm about to explain can be gleaned from #ircmaxwell's excellent slidedeck: Cryptography For The Average Developer which you should check out immediately. I'll reiterate one of his main points: Avoid writing code that deals with encryption/decryption. Unless you understand all the factors you will probably flub it.
MCRYPT_RIJNDAEL_128 closely matches AES
The Advanced Encryption Standard was created by The National Institute of Standards and Technology. NIST chose the Rijandael cipher from a competitive pool of cryptographic experts.
Common confusion: the numbers in AES and Mycrpt refer to different things
AES-128 refers to 128 bit key size
AES-256 refers to 256 bit key size
MCRYPT_RIJNDAEL_128 refers to a 128 bit cipher block size
MCRYPT_RIJNDAEL_256 refers to a 256 bit cipher block size
Key size and block size
Key Size refers to the length of the secret used to encrypt/decrypt. A 256 bit key is 32 bytes, roughly 32 characters.
Block Size is a property of block ciphers (i.e. all AES candidates) which chunk out the data to a specific size during the cipher process.
AES specifies a 128 bit cipher block
Mcrypt's Rijndaels all accept 256 bit keys
based on key/block size MCRYPT_RIJNDAEL_128 seems to conform closest to AES-256
there are other factors to consider — like how many transformation rounds occur? — it's hard to verify this aspect without digging into the source code
Mode of Operation
The mode is an important element.
Both ECB and CBC mode pad your plaintext into the block size. If you're encrypting 1 byte of data with a 128 bit block size you will get 15 null bytes. Padding opens up the possibility for a padding oracle attack.
Beyond padding ECB does not make use of an Initialization Vector (IV) which leaves you open to a potential vulnerability: prefer CBC or CFB mode.
You can avoid both problems by using CFB mode which does not need padding and therefore does not require post-decryption trimming.
Initialization Vector (IV)
When creating your IV you need a crypto-strong random source: MCRYPT_RAND is not random enough — MCRYPT_DEV_RANDOM or MCRYPT_DEV_URANDOM are preferred.
$iv_size = mcrypt_get_iv_size(
MCRYPT_RIJNDAEL_128,
MCRYPT_MODE_CFB
);
$iv = mcrypt_create_iv(
$iv_size,
MCRYPT_DEV_URANDOM
);
Finally, for your friends
In order to decrypt your friend will need to know:
the cipher — MCRYPT_RIJNDAEL_128
the block mode — MCRYPT_MODE_CFB
the IV — each element you encrypt should get a unique IV, so you'll want to append the IV to the encrypted data
the key — this is secret, and should stay the same for all encryptions; if your key gets compromised rotate it out
Only the key needs to stay secret. Depending on your transport method you should consider implementing a data integrity check to ensure the ciphertext data wasn't tampered with. Again, see #ircmaxwell's excellent slidedeck: Cryptography For The Average Developer for an example of creating an HMAC fingerprint using hash_hmac().
It should be obvious that the maintenance all of these moving parts is rife with complexity. Tread carefully.
Other ciphers
Mcrypt gives you other cipher options. Some, like DES, are not recommended. Others were candidates for AES, like Blowfish, TwoFish, and Serpent. Rijandael is a vetted, proven cipher, and is recommended.
MCRYPT_RIJNDAEL_128 is AES-128, MCRYPT_RIJNDAEL_256 is AES-256 - just another name:
[...]The standard comprises three block
ciphers, AES-128, AES-192 and AES-256,
adopted from a larger collection
originally published as Rijndael.originally published as Rijndael.[...]
[...]The Rijndael cipher was developed by
two Belgian cryptographers, Joan
Daemen and Vincent Rijmen, and
submitted by them to the AES selection
process. Rijndael (pronounced "Rhine
dall") is a wordplay with the names of
the two inventors.[...]
The \x00 characters you encounter at the end of the decrypted string are the padding required for some block ciphers (with ECB being such a block cipher). Mcyrpt uses NULL-padding internally if the input data needs to be padded to the required block length. There are other padding modes available (which have to be user-coded when using Mcyrpt), namely PKCS7, ANSI X.923 or ISO 10126. NULL-padding is problematic when encrypting binary data that may end with one or more \x00 characters because you can't detect where the data ends and the padding starts - the other padding modes mentioned solve this kind of problem. If you're encrypting character data (strings) you can easily trim off the trailing \x00 by using $data = trim($data, "\x00");.
To decrypt the data you sent to a consumer, the consumer would need to know the IV (initialization vector) ($iv), the algorithm used (MCRYPT_RIJNDAEL_256/AES-256), the encryption mode (ECB), the secret encryption key ($key) and the padding mode used (NULL-padding). The IV can be transmitted with the encrypted data as it does not need to be kept secret:
The IV must be known to the recipient
of the encrypted information to be
able to decrypt it. This can be
ensured in a number of ways: by
transmitting the IV along with the
ciphertext, by agreeing on it
beforehand during the key exchange or
the handshake, by calculating it
(usually incrementally), or by
measuring such parameters as current
time (used in hardware authentication
tokens such as RSA SecurID, VASCO
Digipass, etc.), IDs such as sender's
and/or recipient's address or ID, file
ID, the packet, sector or cluster
number, etc. A number of variables can
be combined or hashed together,
depending on the protocol.depending on the protocol.
MCRYPT_RIJNDAEL_128 is equivalent to AES-128 in mcrypt. This is documented under MCRYPT_RIJNDAEL_128 here: http://mcrypt.sourceforge.net/

Categories