views:

3935

answers:

2

I need to encrypt a string on the iPhone and send it to a .Net web service for decryption. I am able to encrypt/decrypt on the iPhone and with .Net, but the encrypted strings from the iPhone cannot be decrypted by .Net. The error I get is "Padding is invalid and cannot be removed."

The .Net code is from: http://blog.realcoderscoding.com/index.php/2008/07/dot-net-encryption-simple-aes-wrapper/

The iPhone code uses the sample code from: http://nootech.wordpress.com/2009/01/17/symmetric-encryption-with-the-iphone-sdk/

AFAIK my key settings are the same:

result.BlockSize = 128; // iPhone: kCCBlockSizeAES128
result.KeySize = 128; // kCCBlockSizeAES128
result.Mode = CipherMode.CBC;
result.Padding = PaddingMode.PKCS7; // kCCOptionPKCS7Padding

I tried different ways of generating ciphertext. hello/hello is:

e0PnmbTg/3cT3W+92CDw1Q== in .Net

yrKe5Z7p7MNqx9+CbBvNqQ== on iPhone

and "openssl enc -aes-128-cbc -nosalt -a -in hello.txt -pass pass:hello" generates: QA+Ul+r6Zmr7yHipMcHSbQ==

Update: I've posted the working code for this here.

+8  A: 

At the very least, you are using differing initialization vectors (IV).

  • The .Net code uses the key for IV.

    private static AesCryptoServiceProvider GetProvider(byte[] key)
    {
        //Set up the encryption objects
        AesCryptoServiceProvider result = new AesCryptoServiceProvider();
        byte[] RealKey = Encryptor.GetKey(key, result);
        result.Key = RealKey;
        result.IV = RealKey;
        return result;
    }

    and

    private static byte[] GetKey(byte[] suggestedKey, AesCryptoServiceProvider p)
    {
        byte[] kRaw = suggestedKey;
        List kList = new List();
        for (int i = 0; i < p.LegalKeySizes[0].MinSize; i += 8 )
        {
            kList.Add(kRaw[i % kRaw.Length]);
        }
        byte[] k = kList.ToArray();
        return k;
    }

    which should probably be: kList.Add(kRaw[(i / 8) % kRaw.Length]);. Otherwise a key whose length % 8 == 0 will use the same letter repeatedly, doh!

    Thus the IV (and key) used by .Net is: hleolhleolhleolh. This is not part of the API, but rather due to the wrapper code that you pointed at (which has a serious bug in it...).

  • The iPhone code uses 0 for IV.

    // Initialization vector; dummy in this case 0's.
    uint8_t iv[kChosenCipherBlockSize];
    memset((void *) iv, 0x0, (size_t) sizeof(iv));
  • openssl by default prepends a randomly generated salt (which is why the output is longer!).

The openssl output is more secure since it is prepending a random initialization vector. It looks like the first few bytes of the base64 decoded string is "Salted__". You can also ask openssl to not use a salt (-nosalt) and / or provide an IV (-iv).

Essentially, openssl, .Net, and the iPhone are using the same encryption, you just need to be careful how you initialize the APIs with the encryption key and the initialization vector.

johnny
Thanks!Once I changed the .Net to use the correct key and 0's for the IV, the output is the same. What should the IV be though? Is 0's OK? Would it be more secure if I used something else?
HeroicLife
IV=0 is only OK if you use each key only once. Otherwise you should use a changing IV (a counter, a nonce or a fully random IV).One of the risks with using a static IV is that the same clear-text will generate the same encrypted text each time, which opens up for many attacks.
Anders Westrup
Normally you will use a secure hash algorithm to derive the key and the IV from the password and a random salt. For example, see RFC 2898, which I think uses HMACSHA1.
Cheeso
+1  A: 

Are you sure that you are using the same AES key in your tests? The OpenSSL example in your post uses a password which OpenSSL derives a key and an IV from (and probably uses a salt as well.

Generate a random 128-bit key and specify this key in hex format to OpenSSL with:

openssl enc -aes-128-cbc -a -in hello.txt -K KEY_IN_HEX -iv 0

You shouldn't use IV=0 in any secure system, but for testing interoperability it is OK.

Anders Westrup
this works to decode the iPhone sample using openssl. key = 68656c6c6f (hello).
johnny
Using "hello" as key gives only 40 bits, so the different AES implementations will use some method to increase this to 128 bits. They probably doesn't use the same method for this...Try to specify a full 128bit (16 characters) raw key.
Anders Westrup