I have an encrypted message using MCRYPT_RIJNDAEL_128, ECB mode, and Base64, I've tried decrypting using crypto(https://www.npmjs.com/package/crypto) with the following code:
var text = '';
var decipher = crypto.createDecipheriv("aes-128-ecb","SOMEKEYHEREWITHLENGTH32ooooooooo", '');
text += decipher.update(data, "base64");
text += decipher.final();
The error I've been getting is: Invalid key length 32.
What should the length of key be? When I attempt to use 16 instead the error thrown is:
'TypeError: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt'
If client requires to use ECB with key of length 32, how can I make this case valid?
AES is a subset of Rijndael with a fixed block size 128 bit whereas Rijndael supports block sizes of 128, 192 and 256 bit. The 128 in MCRYPT_RIJNDAEL_128 means the block size. The 128 in aes-128-ecb on the other hand means the key size. Both AES and Rijndael support key sizes of 128, 192 and 256 bit.
If you have a 32 character key and this key is not hex-encoded to reach those 32 characters then it is possible that you actually want aes-256-ecb.
Keep in mind that the key encoding might be different than the one in PHP so might need to parse the key into a Buffer with a specific encoding.
Also, please don't use ECB mode. It is insecure. You should use at least CBC mode with a random IV. Since the IV does not need to be secret, you can simply send it along with the ciphertext as a prefix for example. You would then need to slice it off before decryption.
Related
I need help for decryption algorithm in php.
I got error when I pass wrong encrypt data in my decryption function. so i need to handle error using exception. can it possible?
I tried to this code but no luck
function decrypt($encrypt_data) {
$key = ENC_KEY;
$encryption_key = base64_decode($key);
list($encrypted_data, $iv) = explode('::', base64_decode($encrypt_data), 2);
try {
if(openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv)) {
throw new customException($encrypted_data);
}
}
catch (customException $e) {
echo $e->errorMessage();
}
}
But this function gives me error:-
Warning: openssl_decrypt(): IV passed is only 7 bytes long, cipher
expects an IV of precisely 16 bytes, padding with \0 in abc.php
As Magnus indicates, the key and IV should be specific sizes for AES in CBC mode. The key size should be 32 bytes for AES-256 and the IV is identical to the block size: 16 bytes.
Generally any language will validate the size of the key and IV before using it, but PHP has had the nasty habit (for both the OpenSSL and mcrypt API's) of padding it with zero's if it is too small or cutting part of it when it is too large. This is probably because the underlying C-libraries do not validate the size either as the key and IV are passed as pointers rather than arrays or objects.
To fix this you should make sure that the key is exactly 32 randomized bytes (apparently it is because there is no warning) and that the IV consists of exactly 16 randomized bytes.
Note that it is a security requirement for CBC mode that the IV consists of 16 bytes that are fully unpredictable to an adversary. Just fixing the error by padding the IV with zeros yourself is therefore not recommended. Although the use of 7 bytes as IV is unlikely to directly lead to exploits you should fix the protocol rather than to rely on workarounds in the code.
As indicated, other cryptographic API's (for different languages) will strictly enforce to use keys and IV's of the correct size; if you keep using a 7 byte IV then all those runtimes may need workarounds as well.
I am using a Delphi component from chillkat which does AES encryption for me. It works like a charm and the server accepts my encrypted requests. So I tried to create a php pendant by using mcrypt. But the PHP mcypt result is different in comparison with the Delphi Chillcat result - even if all the parameters are the same. Therefore the server rejects the php requests.
All the encryption settings are the same:
Cipher Name: AES 128
Cipher mode: ECB
Padding scheme: Pad with NULL
Key length: 128
Key: 1234567890ABE1234567890ABE1234DB
String to encrypt: This is a really cool teststring
This is the little php script:
<?php
$key = '1234567890ABE1234567890ABE1234DB';
function string_encrypt($string, $key) {
$crypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_ECB);
return $crypted_text;
}
function string_decrypt($encrypted_string, $key) {
$decrypted_text = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted_string, MCRYPT_MODE_ECB);
return trim($decrypted_text);
}
echo $test_str = 'This is a really cool teststring'; echo '<br />';
$enc_str = string_encrypt($test_str, $key);
echo bin2hex($enc_str); echo '<br />';
echo string_decrypt($enc_str, $key); echo '<br />';
?>
The php output is:
e355fbcd91ada4b835e1b030cc9741759219f59fe441ba62e628eca2e8289eb3
This is the Delphi Code:
function encrypt(s:PWideChar;mode,padding:integer;algo,cipher,keylength:string):string;
var
crypt: HCkCrypt2;
success: Boolean;
ivHex: PWideChar;
keyHex: PWideChar;
encStr: PWideChar;
decStr: PWideChar;
begin
crypt := CkCrypt2_Create();
// AES is also known as Rijndael.
CkCrypt2_putCryptAlgorithm('aes');
// "pki", "aes", "blowfish", "blowfish2", "des", "3des", "rc2", "arc4", "twofish", "pbes1" and "pbes2"
// CipherMode may be "ecb" or "cbc"
CkCrypt2_putCipherMode(crypt,'ecb');
// KeyLength may be 128, 192, 256
try
CkCrypt2_putKeyLength(crypt,128);
Except
showmessage('The encryption key you have used seems to be invalid');
end;
// The padding scheme determines the contents of the bytes
// that are added to pad the result to a multiple of the
// encryption algorithm's block size. AES has a block
// size of 16 bytes, so encrypted output is always
// a multiple of 16.
{
Possible values are:
0 = RFC 1423 padding scheme: Each padding byte is set to the number of padding bytes.
If the data is already a multiple of algorithm's block size bytes, an extra block is
appended each having a value equal to the block size. (for example, if the algorithm's
block size is 16, then 16 bytes having the value 0x10 are added.). (This is also known as
PKCS5 padding: PKCS #5 padding string consists of a sequence of bytes, each of which
is equal to the total number of padding bytes added. )
1 = FIPS81 (Federal Information Processing Standards 81) where the last byte contains
the number of padding bytes, including itself, and the other padding bytes are set to random values.
2 = Each padding byte is set to a random value. The decryptor must know
how many bytes are in the original unencrypted data.
3 = Pad with NULLs. (If already a multiple of the algorithm's block size,
no padding is added).
4 = Pad with SPACE chars(0x20). (If already a multiple of algorithm's block size, no padding is added).
}
CkCrypt2_putPaddingScheme(crypt,3);
// EncodingMode specifies the encoding of the output for
// encryption, and the input for decryption.
// It may be "hex", "url", "base64", or "quoted-printable".
CkCrypt2_putEncodingMode(crypt,'hex');
// An initialization vector is required if using CBC mode.
// ECB mode does not use an IV.
// The length of the IV is equal to the algorithm's block size.
// It is NOT equal to the length of the key.
ivHex := '';
CkCrypt2_SetEncodedIV(crypt,ivHex,'hex');
// The secret key must equal the size of the key. For
// 256-bit encryption, the binary secret key is 32 bytes.
// For 128-bit encryption, the binary secret key is 16 bytes.
keyHex := '1234567890ABE1234567890ABE1234DB';
CkCrypt2_SetEncodedKey(crypt,keyHex,'hex');
// Encrypt a string...
// The input string is 44 ANSI characters (i.e. 44 bytes), so
// the output should be 48 bytes (a multiple of 16).
// Because the output is a hex string, it should
// be 96 characters long (2 chars per byte).
//encryption
if mode = 0 then
begin
encStr := CkCrypt2__encryptStringENC(crypt,s);
result := encStr;
end
else
begin
result := CkCrypt2__decryptStringENC(crypt,s);
end;
CkCrypt2_Dispose(crypt);
End;
The chillkat Delphi component's output is:
780F849AB30690433409D4FB7B3357735296A6E76D3AA6B6D6C769BE99F32041
I thought both outputs should produce the same value, as all the input parameters are equal, right ?
MCrypt expects the key to be a binary string, but you pass a hex encoded string into it.
Use
$key = hex2bin($key);
or
$key = pack('H*', $key);
depending on PHP support. This must be done before calling mcrypt_encrypt().
Security:
Don't ever use ECB mode. It's not semantically secure. There is a lot that an attacker can get from ciphertexts without decrypting them. You need to use at the very least CBC mode with a random IV. You should also have an authentication tag of your ciphertexts.
This answer gives you every thing you need to know about this. One thing left, don't use MCrypt. It's an old unmaintained library.
This answer is also very helpful in that regard with a different way of achieving the same thing.
thanks to your your nice inputs, I was able to receive a positive answer from the server with the following 4 lines of code:
$key = pack('H*', "*5DB");
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,$mytxt, MCRYPT_MODE_ECB);
$hex2 = bin2hex($ciphertext);
echo strtoupper($hex2);
However, I have learned that
ECB is unsecure
mcrypt is old and unmaintained
I will check alternatives and update my code accordingly. Thanks again !
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.
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.
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/