1

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.

Community
  • 1
  • 1
John
  • 91
  • 1
  • 9
  • I would guess you're not using the save IV at both ends, but that is mostly a guess. – Haney Mar 26 '14 at 14:39
  • Thanks, David. I'm certainly attempting to use the same IV (all 0s for now). If I weren't, it seems like I wouldn't be just 16 characters from success. But I will double-check that. – John Mar 26 '14 at 14:43
  • 1
    IV only affects the first block of data; that's why I suspect it. :) – Haney Mar 26 '14 at 15:08
  • Okay, David, I think you're on the right track. On the C# side I get gibberish regardless of what 16-byte IV I use. Please see my Update. Dare I hope that you know how to make those things equivalent? – John Mar 26 '14 at 15:29
  • Yeah, a byte array of 0's is not the same thing as a string of 0's represented in UTF8. You could set your IV to a string of 0's and then convert it to bytes to make them equal. – Haney Mar 26 '14 at 15:49
  • 1
    Thanks for the lead! I'm doing some more research in this direction. – John Mar 26 '14 at 16:46
  • Glad to hear it, I'm making an answer for future Googlers. – Haney Mar 26 '14 at 16:47

1 Answers1

2

If just the first part of your message is garbled, then it's likely that your IV (Initialization Vector) is not identical at the encryption and decryption ends. The IV affects the first block of data, so it makes sense that having the wrong IV would cause your first block to be wrong but the rest to be right.

On one end of your code, a string of "0" characters is used as the IV. At the other end, a byte array of 0-value bytes is used at the IV. These are not the same; a '0' char is not necessarily a 0 byte value. You must make the IVs identical.

Haney
  • 32,775
  • 8
  • 59
  • 68
  • 1
    Thanks!! I discovered that UTF8 encoding of string of 16 0s is 16 hex 30s. So I changed that byte array to 16 48s and got my entire encrypted string back. I should be able to take it from here. :) – John Mar 26 '14 at 17:48