加密/解密iOS的.plist文件

8
我有一个包含某些存储数据的plist文件,想要使用Objective C对其进行加密/解密以防止读取。我已经了解了AES加密等内容,但我希望整个plist文件都能被加密而不仅仅是其中的字符串...
非常感谢任何帮助。

我已经尝试过将http://blog.objectgraph.com/index.php/2010/04/20/encrypting-decrypting-base64-encode-decode-in-iphone-objective-c/转化为二进制文件。 - fizampou
3个回答

5
使用您在评论中提供的链接 https://web.archive.org/web/20150612123348/http://blog.objectgraph.com/index.php/2010/04/20/encrypting-decrypting-base64-encode-decode-in-iphone-objective-c/ 中的代码,您可以通过以下方式加密您的 plist:
NSData *plistFileData = [NSData dataWithContentsOfFile:plistPath];
NSData *encryptedData = [plistFileData AESEncryptWithPassphrase:password];
[encryptedData writeToFile:encryptedPath atomically:YES];

plistPath是包含您想要加密的plist文件路径的NSString。

password是您想要使用的加密密钥。

encryptedPath是您想要保存加密文件的位置。

要解密:

NSData *encryptedData = [NSData dataWithContentsOfFile:encryptedPath];
NSData *plistFileData = [plistFileData AESDecryptWithPassphrase:password];
[plistFileData writeToFile:plistPath atomically:YES];

encryptedPath 是一个包含加密plist文件路径的NSString
password 是您想要使用的加密密钥
plistPath 是您想要保存解密后的plist文件的位置


请注意,如果密码在应用程序内部存储(例如作为字符串),则可以检索密码。 - drct
1
true,然而这已足以防止普通用户读取/修改plist文件。 - howanghk

3

howanghk提供的链接包含有错误的代码。请使用该页面上InoriXu提供的修复程序解决此问题。您需要修改加密和解密函数。

因此,在一行后面:

const char *password = [pass UTF8String];

添加:

const int passwordLen = [pass length];

同时更改行:

key[i] = password != 0 ? *password++ : 0;

into:

key[i] = i < passwordLen != 0 ? *password++ : 0;

代码本身仍然在后面添加了一些空格填充,但如果您需要加密属性列表,这样做就可以了。

链接不是我提供的,而是由用户14356(即原帖作者)在问题的评论中提供的。 - howanghk

3
这里有一个非常简单的答案,希望它能简化问题(如果有的话);
首先,您需要下载 这里 的NSData + AES文件。您只需要NSData + AES.h和NSData + AES.m文件以及cipher.h和cipher.m文件。一旦拿到手,将文件添加到Xcode项目中,并从NSData + AES.h和cipher.h中删除#import Cocoa / Cocoa.h>头文件(仅适用于那些打算为iOS编程的人,如果是MacOS,请让标题保留)。在获取并写入plist文件的文件中导入NSData + AES.h。
现在,基本知识已经奠定,我们接下来将使用这些重要的文件。您需要理解的是加密和解密数据的方式。在第一次运行时,您需要将plist复制到文档文件夹中,然后对其进行加密。请注意,如果您将其复制并尝试直接解密它,它将抛出异常,因此为了处理该异常,我们将使用UserDefaults布尔值,并跳过第一次运行时的解密。另外,您需要定义预处理器指令常量字符串以供加密和解密的密钥使用。下面是您DataHandler类中的示例代码;
    #import <Foundation/Foundation.h>
    #import "NSData+AES.h"
    #define MY_SECRET_KEY   @"MY_SECRET_KEY"

    static NSMutableDictionary *dataDictionary_ = nil;
    static NSMutableDictionary *allSettings_ = nil;

    @implementation DataHandler

    - (id)init
    {
        if(self = [super init])
        {
            [self copyPlistData];
        }
        return self;
    }
    // Encrypt File
    - (NSData*)encryptFile:(NSMutableDictionary *)plistDict
    {
        NSError *err = nil;
        NSData *data = [NSPropertyListSerialization dataWithPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 options:0 error:&err];
        NSData *file = [data encryptWithString:MY_SECRET_KEY];

        return file;
    }

    // Decrypt File
    - (NSMutableDictionary *)decryptFile:(NSData *)data
    {
        NSError *err = nil;
        NSData* newData = [data decryptWithString:MY_SECRET_KEY];
        NSPropertyListFormat format;
        NSMutableDictionary *file = [NSPropertyListSerialization propertyListWithData:newData options:NSPropertyListMutableContainersAndLeaves format:&format error:&err];

        return file;
    }

    - (void) copyPlistData
    {
        NSError *error;
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory =  [paths objectAtIndex:0];
        NSString *path = [documentsDirectory stringByAppendingPathComponent: @"myData.plist"];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        BOOL fileExists = [fileManager fileExistsAtPath:path];

        //check if the file exists already in users documents folder
        //if file does not exist copy it from the APPLICATION bundle Plist file
        if (!fileExists)
        {
            NSLog(@"copying database to users documents");
            NSString *pathToSettingsInBundle = [[NSBundle mainBundle] pathForResource:@"mydata" ofType:@"plist"];
            BOOL copySuccess = [fileManager copyItemAtPath:pathToSettingsInBundle toPath:path error:&error];
            if(copySuccess)
            {
                noCopyError_ = YES;
            }
        }
        //if file is already there do nothing
        else
        {
            noCopyError_ = YES;
            NSLog(@"users database already configured");
        }

        BOOL firstRun = [[NSUserDefaults standardUserDefaults] boolForKey:@"IS_FIRST_RUN"];
        if(noCopyError_ && firstRun)
        {
             dataDictionary_ = [self decryptFile:[NSData dataWithContentsOfFile:path]];
        }
        else
        {
             [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"IS_FIRST_RUN"];
             [[NSUserDefaults standardUserDefaults] synchronize];

             NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
             NSString *documentsDirectory = [paths objectAtIndex:0];
             NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"mydata.plist"];
             dataDictionary_ = (NSMutableDictionary*)[[NSDictionary alloc ] initWithContentsOfFile:plistPath];
             NSMutableDictionary *data = (NSMutableDictionary*)[dictionaryDATA_ objectForKey:@"Data"];

             allSettings_ = [data objectForKey:@"AllSettings"];
        }
    }

    - (NSMutableDictionary*) properties
    {
        NSMutableDictionary * props = [[NSMutableDictionary alloc]init];
        [props setObject: allSettings_ forKey:@"AllSettings"];

        NSMutableDictionary * data = [NSMutableDictionary dictionaryWithObject:props forKey:@"Data"];
        return data;
    }

    - (void)persistData
    {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory =  [paths objectAtIndex:0];
        NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"mydata.plist"];

        NSMutableDictionary *dict = [self properties];
        NSData *encryptedDict = [self encryptFile:dict];
        [encryptedDict writeToFile:plistPath atomically:YES];
    }

但是第一次填充dataDictionary_时,我们必须在AppDelegate.m的didFinishLaunching中强制持久化它:

    DataHandler *dataHandler = [[DataHandler alloc] init];
    [dataHandler persistData];

数据将始终进行加密,但在copyPlist方法中,您将根据dataDictionary_填充模型并与这些模型交互。完成后,您将持久化您的模型并再次加密,因此不会发生任何错误。这是一种简单而实用的解决方案,没有任何麻烦。干杯。


1
你忘了解释iOS开发者在移除 #import Cocoa/Cocoa.h> 头文件时需要添加 #import <Foundation/Foundation.h>。 - Aitul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接