I was given to use the below credentials to decrypt the incoming source in php:
128-bit AES/CBC/PKCS5Padding
Secret key (hex): ec313f73c50b8fcb3ecc473107946b358ddf7a04894be9f6eb3bb0781a6d0d2d
IV (hex, initialization vector): 00000000000000000000000000000000
Mode: CBC
This is what I tried with the warning error:
function decrypt($encrypted_data){
$ciphering = 'aes-128-cbc';
$iv_length = openssl_cipher_iv_length($ciphering);
$options = 0;
$decrypt_iv = '00000000000000000000000000000000';
$decrypt_key = 'ec313f73c50b8fcb3ecc473107946b358ddf7a04894be9f6eb3bb0781a6d0d2d';
$decryption = openssl_decrypt($encryption, $ciphering, $decrypt_key, $options, $decrypt_iv);
return $decryption;
}
Warning: openssl_decrypt(): IV passed is 32 bytes long which is longer
than the 16 expected by selected cipher
would it require extra conversion for the hex secret key and IV?
Yes, use hex2bin() to convert the hex-encoded pieces to raw binary.
The cipherText (encrypted data) is likely somehow encoded as well. That's usually done with Base64, but it might be hex too ... apply appropriate decoding there too.
Also, I seriously hope you didn't just share the real encryption key here.
Related
The code below is a simple .NET snippet, having test on the input it returns p+cTm2VODfvQnreAl02wUQ== as an output.
Dim aesEncryptObj As New System.Security.Cryptography.RijndaelManaged()
Dim encoder As System.Text.ASCIIEncoding = New System.Text.ASCIIEncoding()
Dim tempKey As Byte() = encoder.GetBytes("00000011111111111111111111111111")
aesEncryptObj.Key = tempKey
aesEncryptObj.BlockSize = 128
aesEncryptObj.Mode = System.Security.Cryptography.CipherMode.ECB
aesEncryptObj.Padding = System.Security.Cryptography.PaddingMode.PKCS7
aesEncryptObj.GenerateIV()
Dim EncryptedBytes As Byte()
Dim encryptor As System.Security.Cryptography.ICryptoTransform = aesEncryptObj.CreateEncryptor(aesEncryptObj.Key, aesEncryptObj.IV)
Using msEncrypt As New System.IO.MemoryStream()
Using csEncrypt As New System.Security.Cryptography.CryptoStream(msEncrypt, encryptor, System.Security.Cryptography.CryptoStreamMode.Write)
Using swEncrypt As New System.IO.StreamWriter(csEncrypt)
swEncrypt.Write(txtInput.Text)
End Using
EncryptedBytes = msEncrypt.ToArray()
End Using
End Using
txtOutput.Text = Convert.ToBase64String(EncryptedBytes)
Now, here is the PHP code:
const ENCRYPT_METHOD = 'aes-256-ecb';
$aesKey = pack('H*', '00000011111111111111111111111111');
$ivSize = openssl_cipher_iv_length(ENCRYPT_METHOD);
$plainText = "test";
$iv = openssl_random_pseudo_bytes($ivSize);
$cipherText = openssl_encrypt(
$plainText,
ENCRYPT_METHOD,
$aesKey,
OPENSSL_RAW_DATA,
$iv
);
$encryptedText = $iv . $cipherText;
echo base64_encode($encryptedText);
It returns 1W3UvYVNKWEoFrpPZPd+Qw== which differs from the .NET one. I've tried both aes-256-ecb and aes-128-ecb and the result is always different from the .NET's one.
As far as I know, openssl_encrypt do the PKCS7 padding by default, is that right? Can you see the reason why PHP is giving different result?
Your code isn't working because:
GetBytes in .NET returns you the byte values of that string. That is, you are going to get a byte array with a length of 32 (AES-256). However, pack in PHP with H* decodes a hex string, which is going to give you a key of length 16 (AES-128). This whole time you've been encrypting with not only two different keys, but two different key sizes.
Fixing the above will make your code work, but it will be far from actually secure and should not be used. If you want to make your code secure, you need to:
Stop using ECB mode. Use GCM mode if you can, CBC otherwise. You are most likely going to end up using CBC. If you do, you need to prepend the IV to your ciphertext so that the decrypting party can retrieve it and use it when decrypting. IVs don't need to be secret, they just need to be random, so your use of OpenSSL random bytes and the GenerateIV method is good.
Since you are likely going to be using CBC mode, you will need to apply an HMAC to ensure integrity. Otherwise anyone can modify your ciphertext and you won't know. When applying an HMAC, always encrypt-then-mac. Remember to include your IV before applying the HMAC.
Lastly, and I don't think you actually meant to do this, but never use the ASCII values of a string as a key. If you want to create a key from a passphrase, you should apply a KDF. PBKDF2 is probably the best suited and has implementations in both PHP and .NET.
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);
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 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 am having a heck of a time figuring out how to decrypt a string encrypted with the NSData+AESCrypt.m (Explained here)
I have been looking at a handful of other threads, but I only need the iDevice to send a string to a PHP file encrypted, and then it gets decrypted inside PHP (where it gets stored into a database).
This code :
NSString *encryptedString = [#"Hello" AES256EncryptWithKey:#"a16byteslongkey!"];
NSLog(#"The strign encrypted : %#",encryptedString);
Returns the string encrypted : 7opqbb7sEVNoXplyQv/X8g==
And here is my PHP code for decryption:
function decrypt_data($data, $key) {
return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key,$data,MCRYPT_MODE_ECB);
}
function unpadPKCS7($data, $blockSize) {
$length = strlen ( $data );
if ($length > 0) {
$first = substr ( $data, - 1 );
if (ord ( $first ) <= $blockSize) {
for($i = $length - 2; $i > 0; $i --)
if (ord ( $data [$i] != $first ))
break;
return substr ( $data, 0, $i );
}
}
return $data;
}
function decrypt_string($string) {
$string = unpadPKCS7($string,128);
$string = decrypt_data($string,"a16byteslongkey!");
return $string;
}
die('<br>Basic :'.decrypt_string('7opqbb7sEVNoXplyQv/X8g=='));
UPDATE:
Been doing some MD5 decryption and experimenting a lot, but still far from achieving usable results. This is what I got so far:
Original string : Hello
AES256Encrypt result : 7opqbb7sEVNoXplyQv/X8
base64_decode Decrypted: îŠjm¾ìSh^™rBÿ×
mcrypt_rijndael_128 : Õ¯Öå«Ž(ás2’'u)
mcrypt_rijndael_128 & hex2bin : UÃ)ı+úy´e
Sadly, no matter how I bend and twist this, I just get jibberish. Can anyone see what I'm doing wrong?
Disclaimer: I have zero iPhone development experience.
Short answer - what tc. said. Something is horribly wrong with the AES256EncryptWithKey:
Being AES256 you would expect it to require a 32 byte key, not a 16 byte key. But OK, say it pads shorter keys with null bytes to make them 32 bytes. This might explain why your 16 byte key is being padded with 16 null characters.
But, when it comes to the actual act of encryption, it's using AES 128, but with the 32 byte key. Say wha?
Converting tc.'s Python to PHP:
$base64encoded_ciphertext = '7opqbb7sEVNoXplyQv/X8g==';
$key = 'a16byteslongkey!';
$padded_key = $key . str_repeat(chr(0x00), 16); // Argh!
$result = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $padded_key, base64_decode($base64encoded_ciphertext), 'ecb');
// Yetch - $result ends up being padded with 0x0b's (vertical tab).
var_dump(rtrim($result, chr(0x0b)));
Result:
string(5) "Hello"
~~
Edit: This post from Henno has some relevant details.
~~
Did some additional research. The null padding on your key is likely because AES256 requires a 32 byte key. The 0x0B padding on the plaintext is thanks to PKCS7. PKCS7 is a padding scheme where the byte used for padding is equal in value to the number of bytes added. In this example, 11 bytes were added to the end of 'Hello' turning your 5 byte input into a 16 byte block for AES. 11 = 0x0B.
Thus, the code above will not work when the plaintext is not length = 5. Try the following instead:
$pad_char = ord(substr($result, -1));
$result_without_padding = substr($result, 0, strlen($result) - $pad_char);
The encrypted string looks like it's been base64 encoded. Try decoding it before you decrypt it.
First off, the Objective-C code you're using is pretty terrible:
The keyspace is severely limited (presumably UTF-8 bytes terminated by a null byte, extended with null bytes to 32 bytes). The easiest way to generate a random key is to stick to ASCII, which limits you to about 223.6 bits for the default key size of 256 bits.
Encryption is done in ECB mode.
Data appears to be irreversibly padded with 0x0B.
Avoid it at all costs. It is not secure.
It can be "decrypted" in Python with something like this:
>>> import Crypto.Cipher.AES
>>> import base64
>>> Crypto.Cipher.AES.new('a16byteslongkey!'+'\0'*16).decrypt(base64.b64decode('7opqbb7sEVNoXplyQv/X8g=='))
'Hello\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'
see my post here: PHP iOS AES Encryption
I just got through this same sort of project. I used the library you referenced in "also considered..."
Here is some example code to decrypt with php:
$iv2 = '';
for($i=0;$i<16;$i++){
$iv2 .= "\0";
}
$plain_text_CBC = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted_text, MCRYPT_MODE_CBC, $iv2);
var_dump($plain_text_CBC);
Make sure your keys are both 256-bit (32 characters, I have not yet had any encoding issues, but if you do, remember that you are encrypting bytes, not characters). Note that 128 in MCRYPT_RIJNDAEL_128 is the block size and not the key size, while in the method AES256DecryptWithKey, 256 is a reference to the key size, while the block size is 128. AES256DecryptWithKey runs in CBC mode, but has a null initialization vector (iv).
CBC means that each block depends on the last block, and so it uses a pre-set, usually random, "block -1" called the IV
ECB means that each block is encrypted in the same way, hence it reveals when two blocks in the same message are the same. The library mentioned does not use it, so I mentioned it just for contrast.
The use of a zero iv (0000000000000000 in bytes) is considered insecure, but it does provide you with some additional security (but one might still be able to tell if the fist 16 characters of your plain text were the same each time). To fix this you would have to create an NSData *iv variable for the IV and modify the CCcrypt argument of NSData+AESCrypt.m to add [iv bytes] for the iv parameter (I have not yet tested this code), and you would need to store this iv and pass it to the php along with you message. But first I would test and have everything working with a zero iv.