9

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?

Mathew Thompson
  • 55,877
  • 15
  • 127
  • 148
Nils Munch
  • 8,805
  • 11
  • 51
  • 103
  • Related: [How to do AES256 decryption in PHP?](http://stackoverflow.com/questions/1628138/how-to-do-aes256-decryption-in-php) – hakre Jun 23 '11 at 22:41
  • I have been looking at that as well, and been trying with a direct copy of his decrypt_data function, and using 1234567890123456 as my iv in the encode function... but still no cigar – Nils Munch Jun 23 '11 at 22:57
  • You've seen the pack() comment there? Not that I think it's the solution, but probably worth to be aware of. – hakre Jun 23 '11 at 22:58
  • Yes saw it.. have been running around pack()'ing all the variables i could find (since i can't see where its supposed to be used directly, the $var does not figure in the dialogue) but with no success... :I I will keep experimenting, just wish this was cleaner – Nils Munch Jun 23 '11 at 23:03
  • 1
    Note that ECB mode is not secure in the sense that information still can be learned from your encrypted data, see https://secure.wikimedia.org/wikipedia/en/wiki/ECB_mode#Electronic_codebook_.28ECB.29 for an example. Using CBC or CFB is recommended. – AVH Jun 23 '11 at 23:05
  • see my answer here: http://stackoverflow.com/questions/8438040/php-ios-aes-encryption/8707736#8707736 – user1122069 Jan 03 '12 at 04:04
  • see my post here: http://stackoverflow.com/questions/8438040/php-ios-aes-encryption/8707736#8707736 – user1122069 Jan 03 '12 at 04:05
  • If you don't add integrity protection in a client/server model, you are pretty likely to suffer from a padding oracle attack (where each plain text character is retrieved in max 128 tries on average). – Maarten Bodewes Jul 15 '12 at 21:31
  • Would you mind uploading the category please? I can't access it. – Daniel Oct 22 '12 at 01:51
  • never mind, I found it here! http://pastebin.com/TwPwSLGQ – Daniel Oct 22 '12 at 01:52

4 Answers4

11

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);
Community
  • 1
  • 1
Tails
  • 3,350
  • 2
  • 17
  • 19
  • Oh my numerous gods! It works! Thank you so much, this code will be put in effect, and virtual hugs will reach through the interwebs in your direction ;] – Nils Munch Jun 24 '11 at 10:56
  • That's odd; you'd expect MCRYPT_RIJNDAEL_128 to complain when pssed a 256-bit key. +1 for noticing the PKCS#7 padding which I missed entirely! – tc. Jun 27 '11 at 22:53
  • ECB mode should not be used to encrypt strings. Use CBC with a prepadded random IV or a stream cipher mode. Passwords are not keys, you need a function such as PBKDF2 to create a secure string. Zero padding may work for strings, but for binary data try PKCS#7 padding instead. – Maarten Bodewes Jul 15 '12 at 21:28
  • This code doesn't work in some cases. For example: `"No" -> 2cSCpEVx1LB6lVxTwy901A== -> ok` `"Hello" -> 7opqbb7sEVNoXplyQv/X8g== -> ok` `"Error: 305" -> boaAGQwN1PdyNCywCRW2Nw== -> ok` `"OK" -> ZMedtnsamfYZ9oOnPRC8hw== -> ok` `"400: OK" -> Xc5Sn1tvDMCTNVqCxR9D6w== -> ok` `"500: OK" -> ZvEJ0+qvDHSFapRgJXCI3Q==` -> PHP-code can't decrypt it – user120084 Apr 04 '17 at 10:16
  • Sorry. It was because encoded string had "+" and was processed wrongly. – user120084 Apr 04 '17 at 12:13
2

The encrypted string looks like it's been base64 encoded. Try decoding it before you decrypt it.

tangrs
  • 9,709
  • 1
  • 38
  • 53
  • Please don't ask counter questions in answers. That's what comments are for. – hakre Jun 23 '11 at 22:46
  • putting a $string = base64_decode($string); before the decrypt_data part does make the decrypted string a bit shorter... but still no cigar in the long run... – Nils Munch Jun 23 '11 at 22:58
  • I think you're seeing a combined problem. I'd leave the base64_decode in and try to solve the mcrypt problem. – tangrs Jun 23 '11 at 23:12
  • Im keeping it in and will toss around with some attempts. I have updates the msin thread with my results so far. – Nils Munch Jun 23 '11 at 23:52
1

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'
tc.
  • 33,468
  • 5
  • 78
  • 96
  • Normally I'd gladly listen and follow warnings and advice, but this is a contracted assignment for the local state, and they are very stubborn in how to run this show... I have only been contracted to make this smaller part, and have no influence on the upper level of things (sadly). I have forwarded your warning and your statements but my guess is that they won't move an inch :) – Nils Munch Jun 24 '11 at 10:55
  • That said, it might be worth doing a test with a longer plaintext to distinguish between ECB and some IV-less CBC-alike. In the latter case, it's not *too* terrible provided you prefix the input with 16 random bytes (which is an easy change to make). – tc. Jun 27 '11 at 23:02
0

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.

Community
  • 1
  • 1
user1122069
  • 1,767
  • 1
  • 24
  • 52
  • A zero IV may actually have more implications than just showing if the first block is identical to a previous one. Just an example: if it is identical it could also leak information about the second block and so on. – Maarten Bodewes Jul 15 '12 at 21:30