25

I am encrypting a string in objective-c and also encrypting the same string in Java using AES and am seeing some strange issues. The first part of the result matches up to a certain point but then it is different, hence when i go to decode the result from Java onto the iPhone it cant decrypt it.

I am using a source string of "Now then and what is this nonsense all about. Do you know?" Using a key of "1234567890123456"

The objective-c code to encrypt is the following: NOTE: it is a NSData category so assume that the method is called on an NSData object so 'self' contains the byte data to encrypt.

   - (NSData *)AESEncryptWithKey:(NSString *)key {
 char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
 bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

 // fetch key data
 [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

 NSUInteger dataLength = [self length];

 //See the doc: For block ciphers, the output size will always be less than or 
 //equal to the input size plus the size of one block.
 //That's why we need to add the size of one block here
 size_t bufferSize = dataLength + kCCBlockSizeAES128;
 void *buffer = malloc(bufferSize);

 size_t numBytesEncrypted = 0;
 CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
            keyPtr, kCCKeySizeAES128,
            NULL /* initialization vector (optional) */,
            [self bytes], dataLength, /* input */
            buffer, bufferSize, /* output */
            &numBytesEncrypted);
 if (cryptStatus == kCCSuccess) {
  //the returned NSData takes ownership of the buffer and will free it on deallocation
  return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
 }

 free(buffer); //free the buffer;
 return nil;
}

And the java encryption code is...

public byte[] encryptData(byte[] data, String key) {
    byte[] encrypted = null;

    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    byte[] keyBytes = key.getBytes();

    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

    try {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);

        encrypted = new byte[cipher.getOutputSize(data.length)];
        int ctLength = cipher.update(data, 0, data.length, encrypted, 0);
        ctLength += cipher.doFinal(encrypted, ctLength);
    } catch (Exception e) {
        logger.log(Level.SEVERE, e.getMessage());
    } finally {
        return encrypted;
    }
}

The hex output of the objective-c code is -

7a68ea36 8288c73d f7c45d8d 22432577 9693920a 4fae38b2 2e4bdcef 9aeb8afe 69394f3e 1eb62fa7 74da2b5c 8d7b3c89 a295d306 f1f90349 6899ac34 63a6efa0

and the java output is -

7a68ea36 8288c73d f7c45d8d 22432577 e66b32f9 772b6679 d7c0cb69 037b8740 883f8211 748229f4 723984beb 50b5aea1 f17594c9 fad2d05e e0926805 572156d

As you can see everything is fine up to -

7a68ea36 8288c73d f7c45d8d 22432577

I am guessing I have some of the settings different but can't work out what, I tried changing between ECB and CBC on the java side and it had no effect.

Can anyone help!? please....

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
Simon Lee
  • 22,304
  • 4
  • 41
  • 45

4 Answers4

18

Since the CCCrypt takes an IV, does it not use a chaining block cipher method (such as CBC)? This would be consistant with what you see: the first block is identical, but in the second block the Java version applies the original key to encrypt, but the OSX version seems to use something else.

EDIT:

From here I saw an example. Seems like you need to pass the kCCOptionECBMode to CCCrypt:

ccStatus = CCCrypt(encryptOrDecrypt,
        kCCAlgorithm3DES,
        kCCOptionECBMode, <-- this could help
        vkey, //"123456789012345678901234", //key
        kCCKeySize3DES,
        nil, //"init Vec", //iv,
        vplainText, //"Your Name", //plainText,
        plainTextBufferSize,
        (void *)bufferPtr,
        bufferPtrSize,
        &movedBytes);

EDIT 2:

I played around with some command line to see which one was right. I thought I could contribute it:

$ echo "Now then and what is this nonsense all about. Do you know?" | openssl enc -aes-128-ecb -K $(echo 1234567890123456 | xxd -p) -iv 0 | xxd 
0000000: 7a68 ea36 8288 c73d f7c4 5d8d 2243 2577  zh.6...=..]."C%w
0000010: e66b 32f9 772b 6679 d7c0 cb69 037b 8740  .k2.w+fy...i.{.@
0000020: 883f 8211 7482 29f4 7239 84be b50b 5aea  .?..t.).r9....Z.
0000030: eaa7 519b 65e8 fa26 a1bb de52 083b 478f  ..Q.e..&...R.;G.
Alexander Torstling
  • 18,552
  • 7
  • 62
  • 74
  • I was thinking that but I changed the Java code to the following... Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); ... but it made no difference. Unless I need to change something else to make the Java code use CBC? – Simon Lee Feb 17 '10 at 12:15
  • 1
    Absolutely spot on! Thank you SO much. I can stop banging my head against the wall now :) Cheers again. – Simon Lee Feb 17 '10 at 12:27
  • No problem, glad to be of help. I played around with openssl trying to figure out if Java or Mac was rithg. I added the openssl command line that I used to generate the cipher. I hope it can be of use. – Alexander Torstling Feb 17 '10 at 12:46
  • Argh I have the same problem, identical Objective C code but I can't change the objC side anymore because the data is already encrypted do you know how to solve this changing the java code? See my question: http://stackoverflow.com/questions/3092190/decrypting-data-that-was-aes-encrypted-with-objective-c-with-java – Janusz Jun 22 '10 at 12:42
  • You guys saved me from a self-sustaining nightmare... Thanks! – Shade May 27 '11 at 12:02
  • Hi Guys! Is this algorithm the same as Rijndael 128? I have the same problem for Obj-C and Java. Here's my [post](http://stackoverflow.com/questions/27921093/rewrite-php-rijndael-algorithm-to-java-android) – olleh Jan 14 '15 at 03:09
11

I spent a few weeks decrypting a base64 encoded, AES256 encrypted string. Encryption was done by CCCrypt (Objective-C)on an iPad. The decryption was to be done in Java (using Bouncy Castle).

I finally succeeded and learnt quite a lot in the process. The encryption code was exactly the same as above (I guess it's taken from the Objective-C sample in the iPhone developer documentation).

What CCCrypt() documentation does NOT mention is that it uses CBC mode by default (if you don't specify an option like kCCOptionECBMode). It does mention that the IV, if not specified, defaults to all zeros (so IV will be a byte array of 0x00, 16 members in length).

Using these two pieces of information, you can create a functionally identical encryption module using CBC (and avoid using ECB which is less secure) on both Java and OSx/iphone/ipad(CCCrypt).

The Cipher init function will take the IV byte array as a third argument:

cipher.init(Cipher.ENCRYPT_MODE, keySpec, IV).
Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
user357614
  • 111
  • 1
  • 5
  • The CCCrypt documentation does talk about using CBC by default -- at least I'm pretty sure I saw that somewhere. What I did *not* see anywhere was any mention of defaulting the IV. That was the missing ingredient - thank you! – David Feb 05 '13 at 19:16
  • For anyone else reading this ... you can no longer pass the IV to `Cipher.init()`. What you have to do is (1) obtain an `AlgorithmParameters` object (using the `getInstance()` class factory method, (2) store the the IV in the parameter object : `params.init(new IvParameterSpec(iv));` and (3) pass the `params` object as the third argument to `cipher.init()`. – David Feb 05 '13 at 19:25
9

For anyone else who needs this, disown was absolutely spot on... the revised call to create the crypt in objective-c is as follows (note you need the ECB mode AND the padding)...

CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode + kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES128,
                                          NULL /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesEncrypted);
Simon Lee
  • 22,304
  • 4
  • 41
  • 45
  • Note that for security, CBC mode is considered vastly superior to ECB. ECB leaks information about which input blocks are identical to each other, and such identical blocks happen quite often in "normal data". CBC can be viewed as a kind of data randomization, which makes the occurrence of such blocks much less probable. – Thomas Pornin Feb 17 '10 at 14:49
  • Thanks! I would have spent an entire day trying to figure this out. – ebi Mar 19 '14 at 17:50
  • This helped!! The accepted answer will throw you an error like"more parameters in method call".This should be the answer. – abhimuralidharan Feb 17 '16 at 14:43
2

Just to add to the first post: in your objective C/cocoa code you used CBC mode and in your java code you used EBC and an IV initialization vector wasn't used in either. EBC cipher is block by block and CBC chains upon the preceding block, so if your text is smaller than 1 block (=16 bytes in your example), the cipher text produced by both are decryptable by the other (the same).

If you are looking for a way to standardize your use of the ciphers, NIST Special Publication 800-38A, 2001 Edition has test vectors. I can post code for the AES CBC and EBC vectors if it's helpful to anyone.

Nichole
  • 21
  • 1