3

I have to migrate an Android app to iOS that uses Cipher. So here is the Android code:

PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new DESedeEngine()));
...
byte[] result = Hex.encode(output, 0, output.length);
String resultS = new String(Str.toChars(result));

I tried with lot of stuff for objective-c but can't find a way to get the same string as i get on Java. I used the iOS code from here http://dotmac.rationalmind.net/2009/02/aes-interoperability-between-net-and-iphone/ (and a lot more but all do the same).

And then to get the string on iOS use something like:

NSString* resultS = [encryptedData base64Encoding]

but result strings don't match. Maybe the problem is how i handle the encoding for the NSData (seems that the Java version don't use base64, i'm ok?)

Any ideas?

EDIT 1:

Ok, i made some progress (i hope). Checking the java code they use a block size of 8 and DES/CBC with a key of 24 chars. So i change the code from CocoaFu to this:

- (NSData *)doCipher:(NSData *)dataIn
             key:(NSData *)symmetricKey
         context:(CCOperation)encryptOrDecrypt
{
CCCryptorStatus ccStatus   = kCCSuccess;
size_t          cryptBytes = 0;    // Number of bytes moved to buffer.
NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeDES];
uint8_t iv[kChosenCipherBlockSize];
memset((void *) iv, 0x0, (size_t) sizeof(iv));

ccStatus = CCCrypt( encryptOrDecrypt,
                   kCCAlgorithmDES,
                   kCCOptionPKCS7Padding,
                   symmetricKey.bytes, 
                   kCCKeySize3DES,
                   (const void *)iv,
                   dataIn.bytes,
                   dataIn.length,
                   dataOut.mutableBytes,
                   dataOut.length,
                   &cryptBytes);

if (ccStatus != kCCSuccess) {
    NSLog(@"CCCrypt status: %d", ccStatus);
}

dataOut.length = cryptBytes;

return dataOut;
}

When i try to encode the "test" message in java i get "f69d7c299597c880" but on iOS (using same key of course) i get "< 91864397 > < 41434eaa >" for 3DES and "< ed660859 > < 4bad6f7f >" for DES. Any ideas on what else could i change?

Sebastián Castro
  • 1,408
  • 10
  • 28

3 Answers3

4

This is a difficult problem because it either works or not with very little in clues. The best way to proceed is by starting as simply as possible and building up.

The first thing is to learn exactly what the java code is doing. With CBC mode there will be an iv (initialization value) but none is explicitly specified in the java code posted. You need to find out what the java code is using. Also post the full java code.

From the code PaddedBufferedBlockCipher I infer that there is block padding, this might be PKCS5 or PKCS7, both are the same from a padding point of view, the iOS equivalent is kCCOptionPKCS7Padding. Make sure about this.

Make sure the key length are the same, for AES the options are 128, 192 and 256 bits, unless there are specific reasons use 128.

The code Hex.encode seems to imply that the output is being hex encoded, you will need to do the same on iOS, this is not the same as Base64.

The other main issue is getting all the parameters the same on both sides. Of particular interest are:

  1. encryption key value and size
  2. mode: CBC, ECB, etc. (you should probably be using CBC)
  3. initialization vector (iv) is needed for most modes
  4. padding method: PKCS7, etc. (AES is a block cypher and needs input in a multiple of block size)
  5. Any post encryption processing, hex or Base64 encoding.

Start as simply as possible, iv of all 0, data of one block size, no padding, simple key, no post processing. Get the key, iv and test data from a file that can be shared between systems, this will prevent some errors such as c string jul termination, etc.

Here is the iOS code I use:

#import <CommonCrypto/CommonCryptor.h>

+ (NSData *)doCipher:(NSData *)dataIn
                  iv:(NSData *)iv
                 key:(NSData *)symmetricKey
             context:(CCOperation)encryptOrDecrypt
{
    CCCryptorStatus ccStatus   = kCCSuccess;
    size_t          cryptBytes = 0;    // Number of bytes moved to buffer.
    NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];

    ccStatus = CCCrypt( encryptOrDecrypt,
                       kCCAlgorithmAES128,
                       kCCOptionPKCS7Padding,
                       symmetricKey.bytes, 
                       kCCKeySizeAES128,
                       iv.bytes,
                       dataIn.bytes,
                       dataIn.length,
                       dataOut.mutableBytes,
                       dataOut.length,
                       &cryptBytes);

    if (ccStatus != kCCSuccess) {
        NSLog(@"CCCrypt status: %d", ccStatus);
    }

    dataOut.length = cryptBytes;

    return dataOut;
}

Also add Security.framework to your project.

If the security is important consider having someone with security experience create the code and protocol. If the security is not important, just send the password in the clear.

A few bugs in an app is not that bad, the app still basically works, one bug in security and all security is lost.

Good security is not as easy as one might think--or as my wife says: "If crypto was easy everyone would do it" but she really means correctly.

zaph
  • 111,848
  • 21
  • 189
  • 228
0

Ok, i made some progress (i hope). Checking the java code they use a block size of 8 and DES/CBC with a key of 24 chars. So i change the code from CocoaFu to this:

- (NSData *)doCipher:(NSData *)dataIn
             key:(NSData *)symmetricKey
         context:(CCOperation)encryptOrDecrypt
{
CCCryptorStatus ccStatus   = kCCSuccess;
size_t          cryptBytes = 0;    // Number of bytes moved to buffer.
NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeDES];
uint8_t iv[kChosenCipherBlockSize];
memset((void *) iv, 0x0, (size_t) sizeof(iv));

ccStatus = CCCrypt( encryptOrDecrypt,
                   kCCAlgorithmDES,
                   kCCOptionPKCS7Padding,
                   symmetricKey.bytes, 
                   kCCKeySize3DES,
                   (const void *)iv,
                   dataIn.bytes,
                   dataIn.length,
                   dataOut.mutableBytes,
                   dataOut.length,
                   &cryptBytes);

if (ccStatus != kCCSuccess) {
    NSLog(@"CCCrypt status: %d", ccStatus);
}

dataOut.length = cryptBytes;

return dataOut;
}

When i try to encode the "test" message in java i get "f69d7c299597c880" but on iOS (using same key of course) i get "< 91864397 > < 41434eaa >" for 3DES and "< ed660859 > < 4bad6f7f >" for DES. Any ideas on what else could i change?

Sebastián Castro
  • 1,408
  • 10
  • 28
-1

You're trying to use CBC mode on your cipher, but iOS doesn't support CBC mode, only ECB. Look here

Community
  • 1
  • 1
Barmaley
  • 16,638
  • 18
  • 73
  • 146
  • iOS CommonCrypto does support CBC and CBC the default. – zaph Oct 06 '11 at 12:02
  • Added comment to the SO reference to correct that CBC is supported. – zaph Oct 06 '11 at 12:39
  • I think as CocoaFu says, CBC is the default – Sebastián Castro Oct 06 '11 at 12:46
  • From the Apple header file CommonCryptor.h: Initialization vector, optional. Used by block ciphers when Cipher Block Chaining (CBC) mode is enabled. If present, must be the same length as the selected algorithm's block size. ***If CBC mode is selected (by the absence of the kCCOptionECBMode bit in the options flags)*** and no IV is present, a NULL (all zeroes) IV will be used. – zaph Oct 06 '11 at 13:01
  • @CocoaFu ok, I will know about that – Barmaley Oct 06 '11 at 13:31