我正在尝试将 NSDictionary 保存到 NSUserDefaults ,并使用此帮助程序类Secure-NSUserDefaults使用MD5哈希来检查完整性。
设置字典的代码:
#import "NSUserDefaults+MPSecureUserDefaults.h"
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setSecureObject:aDictionary forKey:aKey];
[defaults synchronize];
检索它的代码:
BOOL valid = NO;
NSDictionary * aDictionary = [defaults secureDictionaryForKey:aKey valid:&valid];
if (!valid) {
//... hash doesn't match
} else {
//... hash matches
}
只要应用程序正在运行(现在在模拟器中测试),这种方法很有效,但是当我退出模拟器并重新启动应用程序时,哈希值与以前不同。
就好像退出应用程序会以某种方式更改字典值(当它保存到磁盘时?)。但是,它并没有添加可见字符,因为它在调试器中看起来完全相同。
非常感谢来自更有经验的程序员的任何想法!
修改
所以这似乎对我有用。想法?
更改NSUserDefaults + MPSecureUserDefaults.m如下:
- (NSString *)_hashObject:(id)object
{
if (_secretData == nil) {
// Use if statement in case asserts are disabled
NSAssert(NO, @"Provide a secret before using any secure writing or reading methods!");
return nil;
}
// Copy object to make sure it is immutable (thanks Stephen)
object = [object copy];
//added check for array or dictionary
if ([NSJSONSerialization isValidJSONObject:object]) {
NSMutableData *archivedData = [[NSJSONSerialization dataWithJSONObject:object options:0 error:nil] mutableCopy];
[archivedData appendData:_secretData];
if (_deviceIdentifierData != nil) {
[archivedData appendData:_deviceIdentifierData];
}
NSString *hash = [self _hashData:archivedData];
return hash;
}
// Archive & hash
NSMutableData *archivedData = [[NSKeyedArchiver archivedDataWithRootObject:object] mutableCopy];
[archivedData appendData:_secretData];
if (_deviceIdentifierData != nil) {
[archivedData appendData:_deviceIdentifierData];
}
NSString *hash = [self _hashData:archivedData];
////[archivedData release];
return hash;
}
答案 0 :(得分:0)
您使用的代码Secure-NSUserDefaults不正确。
代码假设NSKeyedArchiver
的{{1}}无效 - 即如果两个词典相同,那么它们的存档版本是相同的。没有定义字典中键/值对的内部排序,两个字典可以语义相同,而结构不同 - 如果它们在结构上不同,则它们的存档版本他们也可能。
编写自己的或修复您使用过的库。您需要将字典作为键/值对的有序集合来处理 - 例如,在打印它们时,基于archivedDataWithRootObject:
的键进行排序。
HTH
附录:编辑问题后
NSLog
遇到与NSJSONSerialization
相同的问题(对于此用法),因为我在GitHub上发布的简单测试会显示。
看来你可能在这里错过了核心问题。字典是键/值对的无序集合。您正在使用的代码正在尝试为不同的字典生成一个字节序列,这些字节序列相同(或至少产生相同的哈希值),这些字典包含任意顺序的相同的键/值对。由于字典/数组可以包含任何嵌套深度的其他数组/字典,因此问题更加复杂。
生成独立于(内部)排序的字节序列的明显方法是在生成字节序列时命令键/值对。但是,字典键不需要具有排序,只需要相等的关系。
由于密钥没有排序要求,NSKeyedArchiver
和NSKeyedArchiver
不能假设存在,因此不保证为具有相同键/值对的字典生成相同的字节序列(内部到类型)不同。此外NSJSONSerialization
保留对象图,包括任何共享,请参阅Object Graphs,这也可能导致您观察到的差异。
但是,您正在编写属性列表,并且要使字典有效包含在属性列表中,键必须是字符串(请参阅Apple的About Property Lists)。现在字符串确实有一个排序,例如NSKeyedArchiver
的{{1}}方法,因此在这种特殊情况下,您可以订购键/值对。因此,您可以编写自己的代码,也可以找到预先编写的代码,该代码为属性列表类型生成字节流,并在执行此操作时对字典键/值对进行排序;然后你可以在你试图采用的库中使用这段代码。
答案 1 :(得分:0)
只是想知道如何修复这个类:
NSDictionary
应该使用NSKeyedArchiver
进行存档,不仅可以计算哈希值,还可以在NSUserDefaults
中保存(存档)(与直接存储相反)它现在完成了。)
在get方法中,在哈希验证之后,还需要使用NSKeyedUnarchiver
取消归档以获取原始值。
感谢。