23

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.

David Veksler
  • 512
  • 1
  • 5
  • 14
  • I also tried openSSL and it didn't work either: openssl enc -d -aes-128-cbc -a -in crypt.txt -pass pass:hello – David Veksler Feb 11 '09 at 22:05
  • If you pass the -nosalt argument to openssl, you get a similarly sized base64 encoding. It would appear that the .Net and iPhone aren't using an explicit Initialization Vector (salt). But even then, openssl can't decode either. I would guess that each library has it's own predetermined salt? – johnny Feb 12 '09 at 00:00
  • hey boss, i know this is an old question but i'm having problems atm in trying to implement the code you uploaded as I think I have a different way of implementing the aes encryption, I use a IV with a length of 16. Since I'm new with objective , I'm not really sure where that lenght is getting set. – gdubs Jan 31 '13 at 05:23

3 Answers3

15

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
  • 13,113
  • 2
  • 17
  • 8
  • 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? – David Veksler Feb 12 '09 at 17:11
  • 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 Feb 13 '09 at 08:51
  • 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 May 13 '09 at 01:53
  • **The `GetKey` code is not secure**. You should not repeat bytes in a cryptographic key, nor should you remove bytes from it. Instead you should use a Password Based Key Derivation Function (PBKDF, e.g. PBKDF2) or Key Based Key Derivation Function (KBKDF, e.g. hkdf). – Maarten Bodewes Feb 21 '14 at 14:34
  • Thanks man, you save me a lot of time with your solution. I was trying to interop between iPhone and a WCF service. I used all your code, but, in addition, i needed to write the resulting NSData to a base64 string, in order to wrap it into a SOAP envelope. I used the function discussed here: http://stackoverflow.com/questions/882277/how-to-base64-encode-on-the-iphone – Alex R. R. Sep 08 '11 at 22:12
  • This is an old answer, and in case anyone is reading this, **IV must be unpredictable and preferably generated by a cryptographic random number generator, for AES in CBC mode**. Zero IV, static IV, or a counter is not secure when in CBC mode. – Siyuan Ren Jul 02 '15 at 14:20
3

In c#

void test(){
   string ctB64 = encrypt("hola");
   Console.WriteLine(ctB64);  // the same as in objective-c
}

string encrypt(string input)
        {
            try
            {
                // Create a new instance of the AesManaged class.  This generates a new key and initialization vector (IV).
                AesManaged myAes = new AesManaged();

                // Override the cipher mode, key and IV
                myAes.Mode = CipherMode.CBC;
                myAes.IV = new byte[16] { 0x10, 0x16, 0x1F, 0xAD, 0x10, 0x10, 0xAA, 0x22, 0x12, 0x51, 0xF1, 0x1E, 0x15, 0x11, 0x1B, 0x10 }; // must be the same as in objective-c
                myAes.Key = Encoding.UTF8.GetBytes(“0123456789123456”);
                //CipherKey;  // Byte array representing the key
                myAes.Padding = PaddingMode.PKCS7;

                // Create a encryption object to perform the stream transform.
                ICryptoTransform encryptor = myAes.CreateEncryptor();

                // perform the encryption as required...
                MemoryStream ms = new MemoryStream();
                CryptoStream ct = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
                byte[] binput = Encoding.UTF8.GetBytes(input);
                ct.Write(binput, 0, binput.Length);
                ct.Close();
                byte [] result = ms.ToArray();
                return Convert.ToBase64String(result);
            }
            catch (Exception ex)
            {
                // TODO: Log the error 
                Console.WriteLine(ex);
                throw ex;
            }

        }

· In objective-c, add CocoaSecurity library from https://github.com/kelp404/CocoaSecurity

#import "CocoaSecurity.h"
#import "Base64.h"

…

- (void) test{
 unsigned char bytes[] = { 0x10, 0x16, 0x1F, 0xAD, 0x10, 0x10, 0xAA, 0x22, 0x12, 0x51, 0xF1, 0x1E, 0x15, 0x11, 0x1B, 0x10 }; // must be the same as in c#

    NSData *iv = [NSData dataWithBytesNoCopy:bytes length:16 freeWhenDone:YES];
    NSData* key =   [@"0123456789123456" dataUsingEncoding:NSUTF8StringEncoding];

    CocoaSecurityResult *result = [CocoaSecurity aesEncrypt:@"hola" key:key iv:iv];
   NSLog(@"%@", result.base64); // the same as in c#


    NSData *data = [NSData dataWithBase64EncodedString:result.base64];
    CocoaSecurityResult *result2 = [CocoaSecurity aesDecryptWithData:data key:key iv:iv];

    NSLog(@"%@", result2.utf8String); // show "hola"

}
2

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
  • 825
  • 5
  • 6
  • this works to decode the iPhone sample using openssl. key = 68656c6c6f (hello). – johnny Feb 12 '09 at 08:06
  • 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 Feb 12 '09 at 08:41
  • Finally. Worked like a charm. Thank you very much. – reinaldoluckman Mar 17 '11 at 14:39