在NSUserDefaults中保存NSDictionary - 哈希失败

时间:2014-02-09 21:00:41

标签: ios objective-c hash nsdictionary nsuserdefaults

我正在尝试将 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;
}

2 个答案:

答案 0 :(得分:0)

您使用的代码Secure-NSUserDefaults不正确。

代码假设NSKeyedArchiver的{​​{1}}无效 - 即如果两个词典相同,那么它们的存档版本是相同的。没有定义字典中键/值对的内部排序,两个字典可以语义相同,而结构不同 - 如果它们在结构上不同,则它们的存档版本他们也可能。

编写自己的或修复您使用过的库。您需要将字典作为键/值对的有序集合来处理 - 例如,在打印它们时,基于archivedDataWithRootObject:的键进行排序。

HTH

附录:编辑问题后

NSLog遇到与NSJSONSerialization相同的问题(对于此用法),因为我在GitHub上发布的简单测试会显示。

看来你可能在这里错过了核心问题。字典是键/值对的无序集合。您正在使用的代码正在尝试为不同的字典生成一个字节序列,这些字节序列相同(或至少产生相同的哈希值),这些字典包含任意顺序的相同的键/值对。由于字典/数组可以包含任何嵌套深度的其他数组/字典,因此问题更加复杂。

生成独立于(内部)排序的字节序列的明显方法是在生成字节序列时命令键/值对。但是,字典键不需要具有排序,只需要相等的关系。

由于密钥没有排序要求,NSKeyedArchiverNSKeyedArchiver不能假设存在,因此不保证为具有相同键/值对的字典生成相同的字节序列(内部到类型)不同。此外NSJSONSerialization保留对象图,包括任何共享,请参阅Object Graphs,这也可能导致您观察到的差异。

但是,您正在编写属性列表,并且要使字典有效包含在属性列表中,键必须是字符串(请参阅Apple的About Property Lists)。现在字符串确实有一个排序,例如NSKeyedArchiver的{​​{1}}方法,因此在这种特殊情况下,您可以订购键/值对。因此,您可以编写自己的代码,也可以找到预先编写的代码,该代码为属性列表类型生成字节流,并在执行此操作时对字典键/值对进行排序;然后你可以在你试图采用的库中使用这段代码。

答案 1 :(得分:0)

只是想知道如何修复这个类:

NSDictionary应该使用NSKeyedArchiver进行存档,不仅可以计算哈希值,还可以在NSUserDefaults中保存(存档)(与直接存储相反)它现在完成了。)

在get方法中,在哈希验证之后,还需要使用NSKeyedUnarchiver取消归档以获取原始值。

感谢。