AES-256 encryption on iOS not producing same result as openssl

4.3k views Asked by At

I have been looking and looking at this for hours. I am desperately trying to get iOS to encrypt a short piece of text using AES-256 encryption that can then be decrypted by openssl.

Straight forward? Nope.

The code I've found for iOS is not compatible with the keys and IVs for openssl, so I've had to adapt it, but it's plainly not working.

So here is the code to encrypt I am using... passing in a string to encrypt (dataString) a string key (key) and a string initialisation vector (iv)...

- (NSData *)AES256Encrypt:(NSString *)dataString WithKey:(NSString *)key iv:(NSString *)iv {

    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    //char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    //bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

    // fetch key data
    //[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    //NSLog(@"keyPtr: '%s'", keyPtr);

    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
    NSLog(@"keyPtr: '%s'", keyData.bytes);
    NSData *dataToEncrypt = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    NSData *ivData = [iv dataUsingEncoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [dataToEncrypt length];

    //See the doc: For block ciphers, the output size will always be less than or 
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                          keyData.bytes, kCCKeySizeAES256,
                                          ivData.bytes, // initialisation vector
                                          dataToEncrypt.bytes, 
                                          dataToEncrypt.length, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free(buffer); //free the buffer;
    return nil;
}

For the same string to encode, this does not produce the same value as when using openssl with the same key and iv... e.g. this command line:

openssl enc -aes-256-cbc -e -in secrets.txt -a -iv 0000 -K 0000 -p

secrets.txt is just a text file containing the string to be encrypted

This outputs something like this:

salt=3C66000000000000
key=0000000000000000000000000000000000000000000000000000000000000000
iv =00000000000000000000000000000000
qTMfgtAxbF8Yyh27ZDrcIQ==

And to decrypt, do the opposite operation (assuming the encrypted last line of data above is in test.secrets.out)

openssl enc -aes-256-cbc -d -in test.secrets.out -a -iv 0000 -K 0000 -p 
salt=3C66000000000000
key=0000000000000000000000000000000000000000000000000000000000000000
iv =00000000000000000000000000000000
< text of the secrets.txt file >

Now, if I use the key and iv of 4 chars, this doesn't encode correctly in iOS. If I use the full length key and iv, this doesn't encode correctly either.

Basically, this is a check to see that if I send a piece of encrypted data it is the right piece of data.

What am I missing?

Some code I've looked through to try to find an answer...

http://robnapier.net/blog/aes-commoncrypto-564

https://github.com/rnapier/RNCryptor

http://pastie.org/426530

Have searched extensively on here too and cannot find an answer.

Any help appreciated.

1

There are 1 answers

0
Rob Napier On

OpenSSL has a unique (read "not close to any standard, and also not particularly secure") method for converting passwords into IV and key. If you look at RNOpenSSLCryptor, you'll see the algorithm used:

// For aes-128:
//
// key = MD5(password + salt)
// IV = MD5(Key + password + salt)

//
// For aes-256:
//
// Hash0 = ''
// Hash1 = MD5(Hash0 + Password + Salt)
// Hash2 = MD5(Hash1 + Password + Salt)
// Hash3 = MD5(Hash2 + Password + Salt)
// Hash4 = MD5(Hash3 + Password + Salt)
//
// Key = Hash1 + Hash2
// IV = Hash3 + Hash4
//

// File Format:
//
// |Salted___|<salt>|<ciphertext>|
//

Using RNOpenSSLCryptor allows RNCryptor support for the OpenSSL format. I am currently in the midst of reworking this code on the async branch to support async operations, and that branch doesn't yet support OpenSSL, but I do plan to rework that shortly (by mid-July 2012).

If you want code that implements this for your own use, look at keyForPassword:salt: and IVForKey:password:salt:.

Note that the the OpenSSL file format has several security problems and I don't recommend it if you can avoid it. It does not use a very good KDF to generate its key, does not have as random an IV as it should, and provides no HMAC for authentication. These security problems are why I designed a different file format, much as I hated creating "yet another incompatible container."