I need to encrypt a string (an XML file, actually) on an iPhone or iPad and then decrypt it with a .Net application. Thanks to David Veksler's question here, AES interoperability between .Net and iPhone?, and blog post here, http://automagical.rationalmind.net/2009/02/12/aes-interoperability-between-net-and-iphone/, I think I am quite close to accomplishing this.
But in the decrypted string (XML) returned by the C# method, the first 16 characters are gibberish. Beginning with the 17th character, the decrypted string matches the string that was encrypted by the objective-c method.
I followed David's code as closely as possible, but may have changed a couple of things after some trial and error. Here is the encryption code (the password and initVector are just hard-coded in there for now):
CCCryptorStatus result = CCCryptorCreate(kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding, // 0x0000 or kCCOptionPKCS7Padding
(const void *)[@"1234567891123456" dataUsingEncoding:NSUTF8StringEncoding].bytes,
[@"1234567891123456" dataUsingEncoding:NSUTF8StringEncoding].length,
(const void *)[@"0000000000000000" dataUsingEncoding:NSUTF8StringEncoding].bytes,
&thisEncipher
);
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t remainingBytes = 0;
size_t movedBytes = 0;
size_t plainTextBufferSize = 0;
size_t totalBytesWritten = 0;
uint8_t *ptr;
NSData *plainText = [xmlFileText dataUsingEncoding:NSASCIIStringEncoding];
plainTextBufferSize = [plainText length];
bufferPtrSize = CCCryptorGetOutputLength(thisEncipher, plainTextBufferSize, true);
bufferPtr = malloc(bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
ptr = bufferPtr;
remainingBytes = bufferPtrSize;
result = CCCryptorUpdate(thisEncipher,
(const void *)[plainText bytes],
plainTextBufferSize,
ptr,
remainingBytes,
&movedBytes
);
ptr += movedBytes;
remainingBytes -= movedBytes;
totalBytesWritten += movedBytes;
result = CCCryptorFinal(thisEncipher,
ptr,
remainingBytes,
&movedBytes
);
totalBytesWritten += movedBytes;
if (thisEncipher)
{
(void) CCCryptorRelease(thisEncipher);
thisEncipher = NULL;
}
if (result == kCCSuccess)
{
NSData *encryptedData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten];
[[encryptedData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn] writeToFile:docFile atomically:NO encoding:NSUTF8StringEncoding error:nil];
NSLog(@"%d:%d:%d:%@:%@", xmlFileText.length,
encryptedData.length,
[encryptedData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn].length,
encryptedData,
[encryptedData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]);
if (bufferPtr)
free(bufferPtr);
return;
}
And here is the decryption code:
public static string DecryptString(string base64StringToDecrypt, string passphrase)
{
//Set up the encryption objects
using (AesCryptoServiceProvider acsp = GetProvider(Encoding.Default.GetBytes(passphrase)))
{
byte[] RawBytes = Convert.FromBase64String(base64StringToDecrypt);
ICryptoTransform ictD = acsp.CreateDecryptor();
//RawBytes now contains original byte array, still in Encrypted state
//Decrypt into stream
MemoryStream msD = new MemoryStream(RawBytes, 0, RawBytes.Length);
CryptoStream csD = new CryptoStream(msD, ictD, CryptoStreamMode.Read);
//csD now contains original byte array, fully decrypted
//return the content of msD as a regular string
return (new StreamReader(csD)).ReadToEnd();
}
}
From spot-comparing a few, it appears that the NSData, encryptedData contains the same values as the byte[]
, RawBytes. But the XML string returned after StreamReader.ReadToEnd()
matches the NSString
, xmlFileText
, except for the first 16 characters. I suspect the problem is either the way I'm encrypting to obtain NSData
*encryptedData, or the way I'm converting that to a Base64-Encoded String and writing that to the file, or the way I'm decrypting byte[]
RawBytes, or the way I'm converting the decrypted csD back to a string. If anyone can see where I'm going wrong, I will appreciate it.
Update: After David's comments I'm taking a closer look at the IV. I'm trying to use 16 zeros for now.
On iOS, I'm using:
(const void *)[@"0000000000000000" dataUsingEncoding:NSUTF8StringEncoding].bytes
And on .Net I'm using:
new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
These may not be equivalent.