AES Rijndael on PHP server and iOS generates sometimes different ciphers - php

I'm using NSData+AESCrypt category by Jim Dovey and NSString+AESCrypt by Michael Sedlaczek (2011-02-22).
And on PHP I have a simple script:
<?php
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = '01234567890123456789012345678901';
$plaintext = "myworda";
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_ECB);
$base64encoded_ciphertext = base64_encode($ciphertext);
echo "ciphertext: ".$base64encoded_ciphertext."<br/>";
?>
In ObjC:
NSString *key = #"01234567890123456789012345678901";
NSString *plaintext = [#"+l56Ia4yyK19D2x2+oCXuw==" AES256DecryptWithKey: key];
NSLog(#"plaintext: %#", plaintext);
I change the variable $plaintext in PHP, run script and copy and paste the output-cipher to the Objective-c to decrypt it.
And:
"myword" gives me "+l56Ia4yyK19D2x2+oCXuw==" and I decrypt in and get "myword" on iOS [OK]
"good morning" gives me "5UdImsV1pQs60ovXmH74HQ==" and I decrypt in and get "good morning" on iOS [OK]
"Schröder" gives me "KqNSCE8nGsYUYVdGZ2tnMw==" and I decrypt in and get "Schröder" on iOS [OK]
"Schröder" gives me "KqNSCE8nGsYUYVdGZ2tnMw==" and I decrypt in and get "Schröder" on iOS [OK]
"very long text" gives me "lsa+QF3IHQnAFiOjl2Heyg==" and I decrypt in and get "very long text" on iOS [OK]
"very very very very long text" gives me "kl/ThEyuyUMmKSqU4/fJSzzJOyvsXrGRt5/zsnqjQww=" and I cannot decrypt it on iOS [FAIL]
Why #5 fails? If I try to encrypt "very very very very long text" using Xcode, I get "kl/ThEyuyUMmKSqU4/fJS90UZoJ73S4gox2uCoWoIL8=" notice that:
kl/ThEyuyUMmKSqU4/fJS == kl/ThEyuyUMmKSqU4/fJS
zzJOyvsXrGRt5/zsnqjQww != 90UZoJ73S4gox2uCoWoIL8=
But going further, encrypting "good morning" on Xcode gives me "hVq1AuR8PAXSOztK26pmMw==", while PHP gave "5UdImsV1pQs60ovXmH74HQ==", but Xcode decrypts both to "good morning" using the same key.
Please help.

Your PHP code is using ECB mode. I cannot see where you are setting the mode in ObjC. Presumably you are using its default mode. It is possible that the ObjC default mode is not ECB, more likely CBC. Also notice that whenever your plaintext is 16 bytes or less (that is one block or less) the decryption works. When it is greater than 16 bytes (that is it extends into a second block) it fails.
I suspect that the ObjC default is CBC mode with a zero IV. That will act as if it is ECB for the first block only, and differ for the second and subsequent blocks.
ECB mode is unsafe and leaks information. Use CBC mode with a specified IV instead. At the very least change your PHP code to use CBC mode instead of ECB mode.

You encrypt the message using MCRYPT_RIJNDAEL_128 which gives you a block size of 128 bits, or 16 bytes.
You'll notice that very very very very long text is larger than 16 bytes.
So I'm guessing you're decryption on ios uses a different block size. I'm not familiar with ios, but AES256DecryptWithKey seems to indicate it uses a block size of 256 bits.
Try using MCRYPT_RIJNDAEL_256 in your PHP code, or change the ios code to use AES128DecryptWithKey (I don't know if that exist, again, I don't know ios)

Related

Getting extra garbage with AES 256 decrypt (php, phpseclilb, openssl)

I have the following PHP code:
require('/var/www/third_party_plugins/phpseclib/vendor/autoload.php');
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Crypt\RSA;
use phpseclib3\Math\BigInteger;
use phpseclib3\Crypt\AES;
use phpseclib3\Crypt\Random;
$message = hex2bin('f5f905e8b2d8f0a72e179a169a59bc373021a75865e55c6797627bc43ddc6af0d9bd673bf94f5e8defc5af81019fd87c7d504a6aa758ba1e2f1f9858d0293b0b');
$key = hex2bin('d2ce45fd5f80c15db0a4ab26a7e27f42b507ed9469f0d63c1dbe4f89ed84c0c2');
$iv = hex2bin('db9d7e844b00282327221bb563639f96');
$cipher = new AES('cbc');
$cipher->setIV($iv);
$cipher->setKey($key);
//$cipher->disablePadding();
$Decrypted = bin2hex($cipher->decrypt($message));
print("\n" . $Decrypted . "\n");
When I run this code, I get the following result:
240dcbefc0f82fadc00ef8494488aaa81400000c2def01e79fec6c4d9a822358dd8a910cac606e8afcb607793cb442093a56b7b40b
Inside of this result, I can see the message I WANT, which is:
1400000c2def01e79fec6c4d9a822358
However, there are 16 bytes of data in the front of the message which make no sense to me, it seems like some kind of padding, but I dont want it in my result nor do I understand why it is there, because from what I understand the padding should be removed by phpseclib or openssl functions
240dcbef c0f82fad c00ef849 4488aaa8
I understand that there is a 20 byte MAC at the end, however, I notice ONE extra byte in front of the mac:
dd8a910cac606e8afcb607793cb442093a56b7b40b // THIS IS 21 BYTES, NOT 20...why?
I'm also having trouble re-encrypting the data and getting a valid response from the server, as I'm re-encrypting it with PHP, then sending the SAME data to my C# server, and it is unable to decrypt the message.

Encrypt Decrypt Python PHP convert AES

I have this issue where something is encrypted in python using aes 256 cbc encryption as shown in the python codes encrypt method .
I am trying to create a Decrypt method in php to actually decrypt whats encrypted using the python class .
Here is my attempt to convert the python decryption method to php does it look right or am I missing something in my conversion as every time i use the php version to decrypt it says hmac failed ?
anyhelp in converting the python class to php i will appreciate.
public function decrypt(){
$encrypt_method ="AES-256-CBC";
$secret_key =base64_decode('samekeyusedintheencryption');
$encrypted=(string)'some encrypted text to be decrypted';
$data=json_decode(base64_decode($encrypted),true);
$secret_iv =base64_decode($data['iv']);
$output = \openssl_decrypt($data['value'],
$encrypt_method,$secret_key,0,$secret_iv);
return json_encode($output);
}
def decrypt(self, payload):
data = json_c.decode(base64.b64decode(payload))
value = base64.b64decode(data['value'])
iv = base64.b64decode(data['iv'])
crypt_object=AES.new(self.key,AES.MODE_CBC,iv)
plaintext = crypt_object.decrypt(value)
return loads(plaintext)
OK, I got it to work!
function decrypt($encryptedText, $secret_key){
$secret_key = base64_decode($secret_key);
$encrypt_method ="AES-256-CBC";
$data = json_decode(base64_decode($encryptedText),true);
$data['iv'] = base64_decode($data['iv']);
$data['value'] = base64_decode($data['value']);
return openssl_decrypt($data['value'], $encrypt_method, $secret_key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $data['iv']);
}
Some things I learned:
If the options in the openssl function are set to '0' it expects a base64_encoded input for the cipher text. Also, if the default options is set to '0' the padding default is set to PKCS#7. This, I think, is why we were getting the bad block size error.
So, the cipher text needs to be base64_decoded and we need to set both options for the padding.
I was able to decrypt your provided cipher text and see the email addresses.
You are provided the MAC in the Data array so this would allow you to check the MAC in the PHP script. This allows you to make sure the data has not been tampered with.
I recently did an encryption project and started with the open ssl, but ended up changing to the libSodium library. I highly recommend you check it out for any further projects.
Cheers!

AES-256-CBC Encryption/Decryption hex string using openssl in php

I'm a noob developer and I'm trying to decrypt some data but when I use OpenSSL in PHP I received no response. Also, when trying to encrypt data OpenSSL add a block of characters.
This is the code:
<?php
$dataToDecrypt = hex2bin("C2E5CDFE8BBFBC7350D40538434824DD3E11520B89A5BFDE24FA064DB2EED6EA");
$aesKey = hex2bin("E3FB8EA130722FA99266B96B77C2735C39393939393939393920202020202020");
$iv = hex2bin("00000000000000000000000000000000");
$result = openssl_decrypt($dataToDecrypt, 'AES-256-CBC', $aesKey, OPENSSL_RAW_DATA, $iv);
echo bin2hex($result);
?>
The $result is supposed to be:
C3A6149C73FFBE4EAD36DC62FE40877D17CD690F37B06058CA3D65A345CC8212
I've tried this on VB and even in a AES encription web page (http://aes.online-domain-tools.com/) and the result is correct. But when trying with PHP I've got no answer.
I noticed when encrypting with the same information, the encrypted data is different. This is the code:
<?php
$dataToEncrypt = hex2bin("C3A6149C73FFBE4EAD36DC62FE40877D17CD690F37B06058CA3D65A345CC8212");
$aesKey = hex2bin("E3FB8EA130722FA99266B96B77C2735C39393939393939393920202020202020");
$iv = hex2bin("00000000000000000000000000000000");
$result = openssl_encrypt($dataToEncrypt, 'AES-256-CBC', $aesKey, OPENSSL_RAW_DATA, $iv);
echo bin2hex($result);
?>
When I encrypt the result is:
C2E5CDFE8BBFBC7350D40538434824DD3E11520B89A5BFDE24FA064DB2EED6EA3A3ED407DC78D6AF9030BAB90CB40EAD
I get 32 characters more than expected (3A3ED407DC78D6AF9030BAB90CB40EAD). When I encrypt in VB or using the web page mentioned before I don't get these 32 extra characters.
Why is this happening? Am I missing something? I've been searching for an answer for several days. Any help is appreciated.
To see something during PHP decrypt you need to turn on warning messages.
Essentially the openssl_decrypt call will first decrypt your ciphertext. As long as your ciphertext is a multiple of 16 bytes (the block size of AES) this will always succeed. After that it will try and perform PKCS#7 compatible unpadding, which will fail (with high probability). To make it not unpad, use the OPENSSL_ZERO_PADDING in addition to OPENSSL_RAW_DATA.
The same goes for your encryption function of course. Currently you receive the ciphertext of the padded plaintext. This will add exactly one block of padding if the input plaintext is a multiple of the block size (and it is in your sample code). So you need OPENSSL_ZERO_PADDING there as well.

Encrypto in JavaScript and Decrypto in PHP

I want to encrypt in the javascript. decrypt in the PHP.
JavaScript AES crypto library is CryptoJS.
PHP AES crypto library is mcrypt.
I was get incorrect result.
get result like 'I4��L$�"�"E̹_��zHe����V���:�'.
encrypt (JavaScript)
encrypt = CryptoJS.AES.encrypt('HelloWorld', 'test', {mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.ZeroPadding });
decrypt (PHP)
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
mcrypt_decrypt(MCRYPT_RIJNDAEL_256, 'test', $encrypt, MCRYPT_MODE_CBC, $iv);
is wrong somewhere?
Thank you.
As noted in the comments above, SSL is the only way you will acutally protect any data because
Without SSL, anyone could make changes to or sniff your Javascript or webpage and so it cannot be trusted.
Javascript really doesn't allow for non-character based data very well. (i.e. hashes, encoded video, etc.) as all data is usually silently converted to utf-16 or utf-8 internally.
This last point is likly what is causing your problem. The AES library on each end encrypts as if it had access to binary data, however, Javascript is telling the transfer that the encrypted data is in a certain characterset. You may want to try utf8decode or play with iconv() to get it converted to true binary data.

Encrypt in PHP, Decrypt in Python

PHP code:
$key = "12345678abcdefgh12345678abcdefgh";
$iv = "12345678abcdefgh";
$plaindata = "This is a test string.";
$enc = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaindata, MCRYPT_MODE_CBC, $iv));
echo($enc);
Result:
QBN0Yue3D9hBrBuD01n5KWG+lv2doMf97cKm/AeusAI=
How can this be decrypted in Python?
Try something like this (altho i do have PyCrypto installed)
from Crypto.Cipher import AES
import base64
AES.key_size=128
iv="your iv"
key="your key"
crypt_object=AES.new(key=key,mode=AES.MODE_CBC,IV=iv)
decoded=base64.b64decode(plain) # your ecrypted and encoded text goes here
decrypted=crypt_object.decrypt(decoded)
This will bring the decoded text but it will be padded with bytes for it to be a size multiple of 16.
You should probably decide on a proper padding scheme and remove it afterwards accordingly
Read the manual, its pretty well documented.
data = base64.b64decode('QBN0Yue3D9hBrBuD01n5KWG+lv2doMf97cKm/AeusAI=')

Categories