14

My app encrypts and decrypts (or it should) an NSString (the text to be encrypted / decrypted) with another NSString (the keyword) using 256-Bit Encryption. When I run my project and run the encrypt method, nothing gets encrypted the textfield just clears itself. Here is the code I have:

-(void)EncryptText {
    //Declare Keyword and Text
    NSString *plainText = DataBox.text;
    NSString *keyword = Keyword.text;

    //Convert NSString to NSData
    NSData *plainData = [plainText dataUsingEncoding:NSUTF8StringEncoding];

    //Encrypt the Data
    NSData *encryptedData = [plainData AESEncryptWithPassphrase:keyword];

    //Convert the NSData back to NSString
    NSString* cypherText = [[NSString alloc] initWithData:encryptedData encoding:NSUTF8StringEncoding];

    //Place the encrypted sting inside the Data Box
    NSLog(@"Cipher Text: %@", cypherText);
}

The header files can be downloaded by clicking this link: ZIP File containing AES Implementation

I have been told that I need to use Base-64 encoding of my string to get any result. If this is true, then how do I do it?

I have also been told that encryption changed in iOS 5, and my app is an iOS 5+ ONLY app. If this is true, then what do I have to do to make this encryption work on iOS 5 or where can I find another AES 256-bit implementation that will work on NSString.

Why doesn't this code produce a result?

Community
  • 1
  • 1
Sam Spencer
  • 8,492
  • 12
  • 76
  • 133

2 Answers2

11

EDIT: The links below refer to an older implementation. The latest version is called RNCryptor.

Your code doesn't use iOS's built-in AES implementation. It has its own custom implementation. AESEncryptWithPassphrase: also incorrectly generates the key, throwing away most of the entropy in the passphrase.

On iOS, you should be using the CCCrypt*() functions for AES. You should also make sure that you understand what is happening in your encryption and decryption routines. It is very easy to write encryption code that looks correct (in that you cannot read the output by inspection), but is extremely insecure.

See Properly encrypting with AES with CommonCrypto for an explanation of the problems with the above implementation, and how to properly use AES on iOS. Note that iOS 5 now has CCKeyDerivationPBKDF available.

There is no requirement to Base-64 encode your string prior to encryption. Base-64 encoding is used in cases where you need to convert binary data into a form that can be easily sent over email or other places where control characters would be a problem. It converts 8-bit binary data in 7-bit ASCII data. That's not necessary or useful here.


EDIT: It is critical that you carefully read the explanation of how to use this code. It is dangerous to simply cut and paste security code and hope it works. That said, the full source to RNCryptManager is available as part of the Chapter 11 example code for iOS 5 Programming Pushing the Limits and may be helpful [EDIT: This is old code; I recommend RNCryptor now, linked at the top of the answer]. The book (which should be available next week despite what the site says) includes a much longer discussion of how to use this code, including how to improve performance and deal with very large datasets.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Where would the code shown at: http://robnapier.net/blog/aes-commoncrypto-564 go? It wouldn't go in its own class because there is nothing to go into nothing a header and implementation, so would it go into the same .m file that my method is in? – Sam Spencer Nov 27 '11 at 18:43
  • I typically put it in a class called `RNCryptManager`, but you could put it anywhere. The code could also be trivially converted to functions rather than class methods. – Rob Napier Nov 27 '11 at 20:07
  • Hmmm... well When I do this, and I put all that code into an RNCryptManager.m, I get 3 errors referring to "No Known Class Method For Selector:" Random, salt, or AES Passsword – Sam Spencer Nov 27 '11 at 21:49
  • Have you created an @interface block defining the methods in question? Please refer also to the edit above at the bottom of the answer. It includes a full example of the RNCryptManager class. – Rob Napier Dec 05 '11 at 21:08
8

NSData with category just fine for AES encryption, I didnt check zip file but this should work for you;

#import <CommonCrypto/CommonCryptor.h>
@implementation NSData (AESAdditions)
- (NSData*)AES256EncryptWithKey:(NSString*)key {
    // '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];

    NSUInteger dataLength = [self 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,
                                          keyPtr, kCCKeySizeAES256,
                                          NULL /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* 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;
}

- (NSData*)AES256DecryptWithKey:(NSString*)key {
    // '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];

    NSUInteger dataLength = [self 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 numBytesDecrypted    = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES256,
                                          NULL /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesDecrypted);

    if (cryptStatus == kCCSuccess)
    {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }

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

Use it wrapper functions like ;

- (NSData*) encryptString:(NSString*)plaintext withKey:(NSString*)key {
        return [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
}

- (NSString*) decryptData:(NSData*)ciphertext withKey:(NSString*)key {
        return [[[NSString alloc] initWithData:[ciphertext AES256DecryptWithKey:key]
                                      encoding:NSUTF8StringEncoding] autorelease];
}
Fatih Donmez
  • 4,319
  • 3
  • 33
  • 45
  • 1
    Note that this implementation is highly insecure since it throws away most of the keyspace, has no IV, and no salt or key-stretching. See http://robnapier.net/blog/aes-commoncrypto-564 for a discussion of why to avoid this commonly-copied piece of code. – Rob Napier Nov 27 '11 at 18:29
  • @tylerdurden You should check the length of the key, because "getCString:maxlength:encoding" will do nothing if key.length > (kCCKeySizeAES256 + 1) and the cryptor will use 32 '\0' as its key. It is dangerous. – MasterBeta Jul 11 '14 at 08:41