-1

I have a strange issue trying to match decryption between an existing iOS app, a .net server, and and Android app that I'm working on. I've checked that my program outputs byte for byte the same encryption as the iOS and decrypts it's own packets perfectly. It appears that the server is able to decrypt the packets sent from the Android app, but when I try to decode the packets from the server I'm getting a BadPaddingException on the Android, whereas the iOS version decodes properly. I've also checked that the Key and IV are byte identical.

edit: I've added the server side code (part of a UDP socket listener) from my client, at first glance it looks like padding has not been defined properly, but my research says the the default is PKCS7, so I'm still confused as to what's causing the problem.

I've tested message lengths (coming from the server) before and after decryption and I see 2 different messages. one is a null keep alive message of 16 bytes before decryption, 0 bytes after decryption. The second message is 128 bytes before decryption and 112 bytes after decryption, in iOS. Both fail to decrypt in Android.

iOS:

+ (NSData*)decryptData:(NSData*)data key:(NSData*)key iv:(NSData*)iv; 
{
    int FBENCRYPT_BLOCK_SIZE = kCCBlockSizeAES128; 
    int FBENCRYPT_KEY_SIZE = kCCKeySizeAES256;

    // setup key
    unsigned char cKey[FBENCRYPT_KEY_SIZE];
    bzero(cKey, sizeof(cKey));
    [key getBytes:cKey length:FBENCRYPT_KEY_SIZE];

    // setup iv
    char cIv[FBENCRYPT_BLOCK_SIZE];
    bzero(cIv, FBENCRYPT_BLOCK_SIZE);
    if (iv) {
        [iv getBytes:cIv length:FBENCRYPT_BLOCK_SIZE];
    }

    NSData* Result = nil;

    // setup output buffer
    size_t bufferSize = [data length] + FBENCRYPT_BLOCK_SIZE;
    void *buffer = malloc(bufferSize);

    // do decrypt
    size_t decryptedSize = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          cKey,
                                          kCCKeySizeAES256,
                                          cIv,
                                          [data bytes],
                                          [data length],
                                          buffer,
                                          bufferSize,
                                          &decryptedSize);
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
    } else {
        free(buffer);
        NSLog(@"[ERROR] failed to decrypt| CCCryptoStatus: %d", cryptStatus);
    }

    return result;  
}

Android:

byte[] decryptData(byte[] data, byte[] key, byte[] iv)
{
    static int FBENCRYPT_BLOCK_SIZE = 16; //(kCCBlockSizeAES128) 
    static int FBENCRYPT_KEY_SIZE = 32; //(kCCKeySizeAES256)        
    // setup key
    byte[] cKey = new byte[FBENCRYPT_KEY_SIZE];
    Arrays.fill(cKey, (byte) 0x00);

    int num = FBENCRYPT_KEY_SIZE;
    if (key.length<num)
        num = key.length;
    System.arraycopy(key, 0, cKey, 0, num);

    // setup iv
    byte[] cIv = new byte[FBENCRYPT_BLOCK_SIZE];
    Arrays.fill(cIv, (byte) 0x00);
    if (iv!=null) {
        System.arraycopy(iv, 0, cIv, 0, iv.length);
    } 

    Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    SecretKeySpec skeySpec = new SecretKeySpec(cKey, "AES");
    IvParameterSpec ivParameterSpec = new IvParameterSpec(cIv);
    aesCipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
    byte[] byteCipherText = aesCipher.doFinal(data);
    return byteCipherText;
}

Server C#:

        public enum AESBitCounts
    {
        AES64Bit = 8,
        AES128Bit = 16,
        AES256Bit = 32
    }
    public static byte[] Encrypt(byte[] RawPayload, byte[] key, AESBitCounts AESBitCount)
    {
        Symmetric sym = new Symmetric(Symmetric.Provider.Rijndael, false);
        //sym.mcrypto.Padding = System.Security.Cryptography.PaddingMode.None;
        sym.IntializationVector = new Data(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
        Data deckey = new Data();
        deckey.MinBytes = (Int32)AESBitCount;
        deckey.MaxBytes = (Int32)AESBitCount;
        deckey.Bytes = key;
        Byte fred = deckey.Bytes[0];
        Data encrypted = sym.Encrypt(new Data(RawPayload), deckey);
        return encrypted.Bytes;
    }
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Chris Howell
  • 29
  • 1
  • 4
  • possible duplicate of [AES encryption makes different result in iOS and Android](http://stackoverflow.com/questions/13044914/aes-encryption-makes-different-result-in-ios-and-android) – Simon Nov 25 '14 at 22:33
  • I had looked at that post previously, but I don't think this is a similar problem, as my results for encoding on iOS and Android are identical, and my IV and Key are identical. I've tested with long data strings and both produce the same bytes and decode their encryption properly. – Chris Howell Nov 25 '14 at 23:35
  • Please show us how you call the methods and how you handle input and output, explicitly the ciphertext. If I had to guess, you are treating it as a string (text) instead of binary. – Maarten Bodewes Nov 26 '14 at 00:25
  • Check the padding with a hex dump, PKCS7 padding is very simple. Sometimes implementers do not add an additional padding block if the input is an exact multiple of the block size--that is incorrect. – zaph Nov 26 '14 at 01:03
  • @Zaph - I have done a hex dump of encrypted data, key, and IV, that's how I can say that they are Byte-for-Byte the same. Same Data going into both, only the Android produces the BadPaddingException error. I do wonder, however, if Android might be sensitive to the issue you mention with an multiple of 16 bytes not getting an extra padding block. I may try some test with 16 & 32 byte blocks to be sure. – Chris Howell Nov 26 '14 at 02:59
  • If the input data is the same **and** the correct length there will be the same output. You did not say if the padding is correct, examine the last block. Are the `iv` and` key` exactly the correct length? – zaph Nov 26 '14 at 03:02
  • The **data** must be the correct length, not just the allocated length, see my previous comment. You need to hex dump the correct lengths from the **server** just prior to decryption and add that to your question. There is no magic, just that all the inputs must be correct. The padding methods must be the same (php is not pkcs7). – zaph Nov 26 '14 at 12:05
  • @Zaph I've added the message lengths to my question, it seems like the padding is correct, and looking at hex dumps all bytes are the same for all items between platforms. – Chris Howell Nov 26 '14 at 16:27
  • @ArtjomB. I only edited the server code for clarity, so that all three take in the same variables. After further investigation I'm added an extra note to the solution. – Chris Howell Dec 03 '14 at 16:01

1 Answers1

1

It turns out that there is one crucial differance between CCCrypt and Cipher, CCCrypt will return whatever data it is able to decrypt, while Cipher, once it get's a padding error, will not return any data. It seems that my client's server side script was mangling the padding block so that any message shorter than 16 bytes was being decrypted on iOS as null, and in the case of the main message was simply dropping the padding block. I was able to duplicate the result of the iOS code by chopping the last 16 bytes before decoding, returning null if no bytes are left, and switching from using Cipher.doFinal to Cipher.update, which doesn't expect a final padding block to be present.

Edit: I actually didn't need to eliminate the last 16 bytes. Just switching from doFinal to update does the trick.

Chris Howell
  • 29
  • 1
  • 4