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.
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.
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"
}
                        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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With