Sam Spencer
Sam Spencer

Reputation: 8608

AES string encryption in Objective-C

My Objective-C app needs to do string encryption (specifically ).

I've found that AES is the most secure encryption methodology available for consumer use. I also have an understanding of how to convert strings to NSData and back... (just a beginner). Unfortunately many webpages and Q&As about encryption with AES are unclear. None of them state how to use the code given.

This may be less of an "encryption" question, and more of a "how do I use these methods" question, so please bear with me.

I've found these example methods to encrypt and decrypt an NSString:

#import "<CommonCrypto/CommonCryptor.h>"
@implementation NSMutableData(AES)

For encryption:

- (NSMutableData *)encryptAES:(NSString *)key {
    char keyPtr[kCCKeySizeAES256+1];
    bzero(keyPtr, sizeof(keyPtr));

    [key getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF16StringEncoding];
    size_t numBytesEncrypted = 0;

    NSUInteger dataLength = [self length];

    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    NSMutableData *output = [[NSData alloc] init];

    CCCryptorStatus result = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, keyPtr, kCCKeySizeAES256, NULL, [self mutableBytes], [self length], buffer, bufferSize, &numBytesEncrypted);
 
    output = [NSMutableData dataWithBytesNoCopy:buffer length:numBytesEncrypted];

    if (result == kCCSuccess) {
        return output;
    }
    return NULL;
}

For decryption:

- (NSMutableData *)decryptAES:(NSString *)key andForData:(NSMutableData *)objEncryptedData {
    char  keyPtr[kCCKeySizeAES256+1];
    bzero( keyPtr, sizeof(keyPtr) );

    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF16StringEncoding];

    size_t numBytesEncrypted = 0;

    NSUInteger dataLength = [self length];

    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer_decrypt = malloc(bufferSize);    
    NSMutableData *output_decrypt = [[NSData alloc] init];
    CCCryptorStatus result = CCCrypt(kCCDecrypt , kCCAlgorithmAES128, kCCOptionPKCS7Padding, keyPtr, kCCKeySizeAES256, NULL, [self mutableBytes], [self length], buffer_decrypt, bufferSize, &numBytesEncrypted);

    output_decrypt = [NSMutableData dataWithBytesNoCopy:buffer_decrypt length:numBytesEncrypted];

    if (result == kCCSuccess) {
        return output_decrypt;
    } 
    return NULL;
}

This is a method I wrote that I would like to use with the above methods:

- (void)encrypt {
    // Convert NSString to NSData so that it can be used to encrypt the Input
    NSString *input = [inputBox text];
    NSData *inputData = [input dataUsingEncoding: NSUTF8StringEncoding];
    // What to do here...?
}

How do I use the encryptAES and decryptAES methods? Where do they go in my implementation file?

Upvotes: 12

Views: 20082

Answers (5)

Gyana prakash Gouda
Gyana prakash Gouda

Reputation: 1

+ (NSString*)encryptFileAtPath:(NSString *)inputFilePath outputPath:(NSString *)outputFilePath password:(NSString *)password {
    // Convert the URL to a local file path
    // Use the converted local file path to read the input data
    NSData *inputData = [NSData dataWithContentsOfFile:inputFilePath];

    if (!inputData) {
        NSLog(@"Failed to read input file.");
        return @"";
    }

    // Generate the key and IV from the password using PBKDF2 (same as in encryption)
    NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableData *keyData = [NSMutableData dataWithLength:kCCKeySizeAES256];
    NSMutableData *ivData = [NSMutableData dataWithLength:kCCBlockSizeAES128];

    // Use the same salt and rounds as in encryption
    const char myByteArray[] = {0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
    NSData *salt = [NSData dataWithBytes:myByteArray length:32];
    unsigned int rounds = 50000;
    CCKeyDerivationPBKDF(kCCPBKDF2, passwordData.bytes, passwordData.length, [salt bytes], [salt length], 0, rounds, keyData.mutableBytes, keyData.length);

    // Set up the decryption context with CBC mode
    CCCryptorRef cryptor;
    CCCryptorStatus status = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding | kCCOptionECBMode,
                                             keyData.bytes, keyData.length, ivData.bytes, &cryptor);
    
    if (status == kCCSuccess) {
        NSMutableData *outputData = [NSMutableData dataWithLength:inputData.length + kCCBlockSizeAES128];
        size_t encryptedLength;

        // Perform encryption
        status = CCCryptorUpdate(cryptor, inputData.bytes, inputData.length, outputData.mutableBytes, outputData.length, &encryptedLength);

        if (status == kCCSuccess) {
            // Finalize encryption
            size_t finalLength;
            status = CCCryptorFinal(cryptor, outputData.mutableBytes + encryptedLength, outputData.length - encryptedLength, &finalLength);
//            [outputData setLength:encryptedLength + finalLength];

            if (status == kCCSuccess) {
                // Write encrypted data to the output file
                NSString *outputFileName = [inputFilePath lastPathComponent];
                NSURL *outPath = [NSURL URLWithString:outputFilePath];

                NSString *trimmedFilePaths = [outPath.absoluteString containsString:@"file://"] == YES? [outPath.absoluteString stringByReplacingOccurrencesOfString:@"file://" withString:@""]: outPath.absoluteString;

                NSString *trimmedD = [trimmedFilePaths containsString:@"%20"] == YES? [trimmedFilePaths stringByReplacingOccurrencesOfString:@"%20" withString:@" "]: trimmedFilePaths;

                NSString *outputFileSPath = [trimmedD stringByAppendingPathComponent:outputFileName];
                [outputData writeToFile:outputFileSPath atomically:YES];
                NSLog(@"File encrypted successfully: %@", outputFilePath);
                return outputFileSPath;
            } else {
                NSLog(@"Encryption finalization error: %d", status);
            }
        } else {
            NSLog(@"Encryption update error: %d", status);
        }

        CCCryptorRelease(cryptor);
    } else {
        NSLog(@"Cryptor creation error: %d", status);
    }

    return @"";
}

Upvotes: 0

Mudassir
Mudassir

Reputation: 47

In my case above given code was not working. It was showing error regarding "kCCBufferTooSmall errorCode = -4301". I have to change it bit,if above code is not working please try it

+ (NSMutableData *)doAES:(NSData *)dataIn context:(CCOperation)kCCEncrypt_or_kCCDecrypt error:(NSError **)error {
    CCCryptorStatus ccStatus   = kCCSuccess;
    size_t          cryptBytes = 0;
    NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCKeySizeAES256];
    NSData *key =[encryptionNewKey dataUsingEncoding:NSUTF8StringEncoding];
    NSData *iv = [encryptionIV dataUsingEncoding:NSUTF8StringEncoding];
    
    ccStatus = CCCrypt( kCCEncrypt_or_kCCDecrypt,
                       kCCAlgorithmAES,
                       kCCOptionPKCS7Padding,
                       key.bytes,
                       kCCKeySizeAES256,
                       iv.bytes,
                       dataIn.bytes,
                       dataIn.length,
                       dataOut.mutableBytes,
                       dataOut.length,
                       &cryptBytes);
    
    if (ccStatus == kCCSuccess) {
        dataOut.length = cryptBytes;
    }
    else {
        if (error) {
            *error = [NSError errorWithDomain:@"kEncryptionError"
                                         code:ccStatus
                                     userInfo:nil];
        }
        dataOut = nil;
    }
    
    return dataOut;
} 

Hope can help someone

Upvotes: 0

Julian Corr&#234;a
Julian Corr&#234;a

Reputation: 668

I got success using AES with the codes below:

Header file

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCryptor.h>

NS_ASSUME_NONNULL_BEGIN

@interface SecurityUtils : NSObject

+ (NSString *)encrypt:(NSString *)plainText error:(NSError **)error;
+ (NSString *)decrypt:(NSString *)plainText error:(NSError **)error;

@end

NS_ASSUME_NONNULL_END

Implementation file

NSString *const IV = @"AEE0515D0B08A4E4";
NSString *const KEY =  @"9336565521E5F082BB5929E8E033BC69";


#import "SecurityUtils.h"


@implementation SecurityUtils


+ (NSString *)encrypt:(NSString *)plainText error:(NSError **)error {
    NSMutableData *result =  [SecurityUtils doAES:[plainText dataUsingEncoding:NSUTF8StringEncoding] context: kCCEncrypt error:error];
    return [result base64EncodedStringWithOptions:0];
}


+ (NSString *)decrypt:(NSString *)encryptedBase64String error:(NSError **)error {
    NSData *dataToDecrypt = [[NSData alloc] initWithBase64EncodedString:encryptedBase64String options:0];
    NSMutableData *result = [SecurityUtils doAES:dataToDecrypt context: kCCDecrypt error:error];
    return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];

}

+ (NSMutableData *)doAES:(NSData *)dataIn context:(CCOperation)kCCEncrypt_or_kCCDecrypt error:(NSError **)error {
        CCCryptorStatus ccStatus   = kCCSuccess;
        size_t          cryptBytes = 0;
        NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeBlowfish];
        NSData *key =[KEY dataUsingEncoding:NSUTF8StringEncoding];
        NSData *iv = [IV dataUsingEncoding:NSUTF8StringEncoding];

        ccStatus = CCCrypt( kCCEncrypt_or_kCCDecrypt,
                           kCCAlgorithmAES,
                           kCCOptionPKCS7Padding,
                           key.bytes,
                           key.length,
                           (iv)?nil:iv.bytes,
                           dataIn.bytes,
                           dataIn.length,
                           dataOut.mutableBytes,
                           dataOut.length,
                           &cryptBytes);

        if (ccStatus == kCCSuccess) {
            dataOut.length = cryptBytes;
        }
        else {
            if (error) {
                *error = [NSError errorWithDomain:@"kEncryptionError"
                                             code:ccStatus
                                         userInfo:nil];
            }
            dataOut = nil;
        }

        return dataOut;
}


@end

IOS TESTING

NSError *error;
NSString *encrypted = [SecurityUtils encrypt:@"My Secret Text" error:&error];
NSLog(@"encrypted: %@",encrypted);
NSLog(@"decrypted: %@",[SecurityUtils decrypt:encrypted error:&error]);

Finally, the tests outputs:

IOS OUTPUT

2019-05-16 21:38:02.947043-0300 MyApp[63392:1590665] encrypted: EJ41am5W1k6fA7ygFjTSEw==
2019-05-16 21:38:02.947270-0300 MyApp[63392:1590665] decrypted: My Secret Text

My repo on github with this examples: https://github.com/juliancorrea/aes-crypto-android-and-ios

Upvotes: 2

Samuel Neves
Samuel Neves

Reputation: 196

Since this appears to have been ignored so far:

CCCryptorStatus result = CCCrypt( kCCDecrypt , kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                 keyPtr, kCCKeySizeAES256,
                                 **NULL**,
                                 [self mutableBytes], [self length],
                                 buffer_decrypt, bufferSize,
                                 &numBytesEncrypted );

From the header file CommonCrypto/CommonCryptor.h:

@param iv Initialization vector, optional. Used by block ciphers when Cipher Block Chaining (CBC) mode is enabled. If present, must be the same length as the selected algorithm's block size. If CBC mode is selected (by the absence of the kCCOptionECBMode bit in the options flags) and no IV is present, a NULL (all zeroes) IV will be used. This parameter is ignored if ECB mode is used or if a stream cipher algorithm is selected.

The NULL in bold corresponds to the IV. Sadly, whoever designed the API made it optional. This makes this CBC mode essentially equivalent to ECB, which is not recommended for a variety of reasons.

Upvotes: 6

Graham Perks
Graham Perks

Reputation: 23398

This line near the top says you're adding AES functionality to NSMutableData:

@implementation NSMutableData(AES)

In Objective-C, this is called a category; categories let you extend an existing class.

This code would typically go in a file named NSMutableData-AES.m. Create a header file too, NSMutableData-AES.h. It should contain:

@interface NSMutableData(AES)
- (NSMutableData*) EncryptAES: (NSString *) key;
@end

Include (#import) that header in your main file. Add a call to the encryption function in your code:

NSData *InputData = [Input dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedData = [InputData EncryptAES:@"myencryptionkey"];

Similarly for decryption.

Upvotes: 7

Related Questions