AES string encryption in Objective-C
Asked Answered
B

5

12

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?

Barnum answered 2/9, 2011 at 22:9 Comment(4)
Hi @RazorSharp - have migrated your question here (Stack Overflow). Questions on getting code working of any sort, cryptographic or otherwise are best answered here as there's a large pool of people here who can help, but if you want to ask about the details of any of the algorithms, paddings etc used, then crypto is the place for it!Amazonas
An important design consideration to keep in mind is that the encryption algorithm is only part of the solution. Key management is very important, and can easily be the weakest point in a system. There are some good books out there on this kind of thing (eg. Schneier), and lots of articles on design. Some research now may save a lot of work later.Carpogonium
Use github.com/Gurpartap/AESCrypt-ObjC for quick AES-256-CBC cipher and Base64 encoding based encryption/decryption. You can use the same cipher and encoding for usage on off-device processing (server, etc).Trichotomy
possibly missing free(buffer) / free(buffer_decrypt) when result != kCCSuccess?Menorrhagia
R
7

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.

Richart answered 3/9, 2011 at 2:8 Comment(3)
YOU are a genius! That works wonderfully. I have a few questions though. First of all, in my NSMutableData-AES.h file, should I import the foundation? #import <Foundation/Foundation.h> I get lots of "Undeclared Identifier" errors in my NSMutableData-AES.m IS this because I have to use the (@)property and (@)synthesize with things like kCCSuccess or CCCryptorStatus? Thank you for your help!Barnum
You also need the code for CCCrypt in your project, and the header for that. A similar implementation is here, all files needed zipped up for ease: iphonedevelopment.blogspot.com/2009/02/…Richart
@GrahamPerks Apologies to jump in on this thread and a bit of a long shot but i have some encryption taking place using CCryptor and need to decrypt using node. Having awful problems, would you be so kind as to take a look at #68137402 please ?Attractant
C
6

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.

Cactus answered 4/9, 2011 at 12:32 Comment(1)
any help in my case #40486018Hennie
S
2

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

Sidran answered 17/5, 2019 at 0:49 Comment(0)
I
0

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

Implication answered 10/6, 2021 at 7:5 Comment(0)
H
0
+ (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 @"";
}
Hautegaronne answered 14/9, 2023 at 6:29 Comment(1)
Hi @Gyana! Welcome to StackOverflow, and thanks for your answer with a great code example. Generally, on StackOverflow you should post an explanation alongside your code so others understand what it does / how to use it / why to use it. Do you mind updating your answer with a little more detail, please?Barnum

© 2022 - 2024 — McMap. All rights reserved.