3

I have found that if I use transformable type of attributes and NSXMLStoreType my data is encrypted, that is attributes that has been of transformable type, are not readable. There is no need of doing anything else, no code is required. Please note that I am working on OS-X application that uses core data.

However, if I change my store type to NSSQLiteStoreType, that is not the case.

I can open the database with sqllitebrowser, select the transformable field, and If I click on export button, in the generated text file, I can read the value normally, that is the value (data) is not encrypted.

I have asked the same question about 4 months ago and I get no answer.

Also, I have found this post here on stackoverflow.

You can encrypt individual properties in your Core Data model entities by making them transformable properties, then creating an NSValueTransformer subclass which will encrypt and decrypt the data for that property.

Unlucky for me, the author of the answer, @Brad Larson, didn't provide an simple example of how this can be done.

Can anyone provide any sample code of how I can encrypt transformable properties so that It' cant be readable in any way?

Community
  • 1
  • 1
user2417624
  • 653
  • 10
  • 32

1 Answers1

6

you could do something like shown here Cross-platform-AES-encryption

add a new Objective-C file to project select category and NSData class call it Additions

NSData+Additions.h

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

@interface NSData (Additions)

#pragma mark - data encryption

+ (NSData *)encrypt:(NSData *)plainText key:(NSData *)key iv:(NSData *)iv;
+ (NSData *)decrypt:(NSData *)encryptedText key:(NSData *)key iv:(NSData *)iv;

+ (NSData *)dataFromHexString:(NSString *)string;
+ (NSData *)sha256forData:(id)input;

+ (NSData *)generateRandomIV:(size_t)length;

@end

NSData+Additions.m

#import "NSData+Additions.h"

@implementation NSData (Additions)

+ (NSData *)encrypt:(NSData *)dataToEncrypt key:(NSData *)key iv:(NSData *)iv {

    NSUInteger dataLength = [dataToEncrypt length];

    size_t buffSize = dataLength + kCCBlockSizeAES128;
    void *buff = malloc(buffSize);

    size_t numBytesEncrypted = 0;

    CCCryptorStatus status = CCCrypt(kCCEncrypt,
                                     kCCAlgorithmAES128,
                                     kCCOptionPKCS7Padding,
                                     [key bytes], kCCKeySizeAES256,
                                     [iv bytes],
                                     [dataToEncrypt bytes], [dataToEncrypt length],
                                     buff, buffSize,
                                     &numBytesEncrypted);

    if (status == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buff length:numBytesEncrypted];
    }

    free(buff);
    return nil;
}

+ (NSData *)decrypt:(NSData *)encryptedData key:(NSData *)key iv:(NSData *)iv {

    NSUInteger dataLength = [encryptedData length];

    size_t buffSize = dataLength + kCCBlockSizeAES128;

    void *buff = malloc(buffSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus status = CCCrypt(kCCDecrypt,
                                     kCCAlgorithmAES128,
                                     kCCOptionPKCS7Padding,
                                     [key bytes], kCCKeySizeAES256,
                                     [iv bytes],
                                     [encryptedData bytes], [encryptedData length],
                                     buff, buffSize,
                                     &numBytesEncrypted);
    if (status == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buff length:numBytesEncrypted];
    }

    free(buff);
    return nil;
}

+ (NSData *)dataFromHexString:(NSString *)string {

    NSMutableData *stringData = [[NSMutableData alloc] init];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};

    for (int counter = 0; counter < [string length] / 2; counter++) {
        byte_chars[0] = [string characterAtIndex:counter * 2];
        byte_chars[1] = [string characterAtIndex:counter * 2 + 1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [stringData appendBytes:&whole_byte length:1];
    }

    return stringData;
}

+ (NSData *)sha256forData:(id)input {
    NSData *dataIn;

    if ([input isKindOfClass:[NSString class]]) {
        dataIn = [input dataUsingEncoding:NSUTF8StringEncoding];
    } else if ([input isKindOfClass:[NSData class]]) {

        NSUInteger dataLength = [input length];
        NSMutableString *string = [NSMutableString stringWithCapacity:dataLength * 2];
        const unsigned char *dataBytes = [input bytes];

        for (NSInteger idx = 0; idx < dataLength; ++idx)
            [string appendFormat:@"%02x", dataBytes[idx]];

        dataIn = [string dataUsingEncoding:NSUTF8StringEncoding];
    }

    NSMutableData *macOut = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];

    CC_SHA256(dataIn.bytes, (CC_LONG)[dataIn length],  [macOut mutableBytes]);

    return macOut;
}

+ (NSData *)generateRandomIV:(size_t)length {
    NSMutableData *data = [NSMutableData dataWithLength:length];

    SecRandomCopyBytes(kSecRandomDefault, length, [data mutableBytes]);

    return data;
}

@end

then you are going to need username, password, iVector and some random salt. in the salt i have used replace # with random characters but make sure that if u use % do not forget to double it %% otherwise it will be incomplete format specifier warning.

in your class use it like this

#define CC_USERNAME         @"secretName"
#define CC_PASSWORD         @"secretPassword"
#define CC_SALTED_STRING    [NSString stringWithFormat:@"####################%@#####################", CC_PASSWORD]

then create NSData representation of your salted string ran through SHA256

NSData *hash = [NSData sha256forData:CC_SALTED_STRING];

next step is to generate 16 bytes of random generated iVector data

NSData *iVector = [NSData generateRandomIV:16];

and use these objects to encrypt your string. create a NSMutableData object with first 16 bytes of iVector data (make sure u use iVector object and do not generate new random or you will be unable to decrypt).

NSString *message = @"my secret message to the world";
NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding];

NSMutableData *encryptedData = [[NSMutableData alloc] initWithData:iVector];

NSData *payLoad = [NSData encrypt:messageData key:hash iv:iVector];

[encryptedData appendData:payLoad];

to decrypt, separate first 16 bytes and the rest of data and use it with the hash.

NSData *pureData = [encryptedData subdataWithRange:NSMakeRange(16, [encryptedData length] - 16)];
NSData *extractedVector = [encryptedData subdataWithRange:NSMakeRange(0, 16)];

NSData *decryptedData = [NSData decrypt:pureData key:hash iv:extractedVector];

NSString *decryptedMessage = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];

you can do some extra md5 on the hash or even pack encrypted data with zlib before storing it.

enjoy your custom made crypto.

ha100
  • 1,563
  • 1
  • 21
  • 28
  • I will try to implement this now...hopefully I will manage to make it to work. I appreciate your time & effort. – user2417624 Mar 27 '16 at 00:47
  • I get a bunch of errors in the file NSData+Base64.m. First the compiler is complaining about the line 25 return [result autorelease]; and after chaning that line in return result; I get warning @implementation NSData (Base64) Method definition base64EncodingStringWithSeparateLines not found, and bunch an errors bellow this line...Just to mention this again....I am working on OS-X application – user2417624 Mar 30 '16 at 01:00
  • i havent used base64. just try the implementation i wrote. that shoul be sufficient for your needs. my methods might be just slightly modified to my needs. but the code i wrote works on its own no need to use base64 – ha100 Mar 30 '16 at 07:09
  • I have one more question....I use binding to insert and read the data. How could I implement your code with binding? – user2417624 Mar 31 '16 at 02:04
  • do you know why the line NSData *hash = [NSData sha256forData:CC_SALTED_STRING]; produces warning "data argument not used by format string" ? – user2417624 Mar 31 '16 at 02:17
  • it might be because my method is ready to take NSData or NSString as an argument. for the implementation i wrote previous it is probably better to use smthng like + (NSData *)sha256forData:(NSString *)string { NSData *dataIn = [string dataUsingEncoding:NSUTF8StringEncoding]; NSMutableData *macOut = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH]; CC_SHA256(dataIn.bytes, (CC_LONG)[dataIn length], [macOut mutableBytes]); return macOut; } – ha100 Mar 31 '16 at 08:17
  • i just figured it out now. you have one extra % sign in CC_SALTED_STRING – ha100 Mar 31 '16 at 08:47
  • great...now the only problem that remain is to find out how to make this code to work with binding...I use binding in all my forms for inserting and reading the data... – user2417624 Mar 31 '16 at 09:58
  • How could I implement your code with binding? I use binding to read and to inset the data. – user2417624 Apr 01 '16 at 02:34
  • i got no idea. neva used binding – ha100 Apr 01 '16 at 05:53