将NSDictionary存储在钥匙串中

mal*_*ois 19 iphone objective-c keychain nsdictionary ios

可以使用(或不使用)NSDictionaryiPhone钥匙串存放在钥匙串中KeychainItemWrapper吗?如果不可能,你有另一种解决方案吗?

Bre*_*asy 25

NSDictionary在将其存储到Keychain之前,必须正确序列化.使用:

[dic description]
[dic propertyList]
Run Code Online (Sandbox Code Playgroud)

你最终会得到一NSDictionary组只有NSString对象.如果要维护对象的数据类型,可以使用NSPropertyListSerialization.

KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"arbitraryId" accessGroup:nil]
NSString *error;
//The following NSData object may be stored in the Keychain
NSData *dictionaryRep = [NSPropertyListSerialization dataFromPropertyList:dictionary format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
[keychain setObject:dictionaryRep forKey:kSecValueData];

//When the NSData object object is retrieved from the Keychain, you convert it back to NSDictionary type
dictionaryRep = [keychain objectForKey:kSecValueData];
NSDictionary *dictionary = [NSPropertyListSerialization propertyListFromData:dictionaryRep mutabilityOption:NSPropertyListImmutable format:nil errorDescription:&error];

if (error) {
    NSLog(@"%@", error);
}
Run Code Online (Sandbox Code Playgroud)

NSDictionary通过第二次调用返回NSPropertyListSerialization将内保持原始数据类型的NSDictionary集合.

  • 这将数据存储在`kSecAttrService`中,它不是加密字段.我相信你的意思是在这里使用`kSecValueData`,这是加密的有效载荷. (4认同)
  • 代码不起作用,为密钥`kSecValueData`传递`NSData`会破坏`KeychainItemWrapper`,因为在内部它期望该密钥的值为NSString(即密码).这是因为它需要加密`kSecValueData`的有效载荷,在它可以这样做之前需要将它转换为`NSData`.因此,`KeychainItemWrapper`已在内部执行`[payloadString dataUsingEncoding:NSUTF8StringEncoding]`,如果您将`NSData`作为`payloadString`传递,您将获得一个`无法识别的选择器发送到实例异常`.有关更多详细信息和解决方案,请查看此页面上的答案. (3认同)

DTs*_*DTs 14

使用KeychainItemWrapper依赖项需要修改库/示例代码以接受NSData作为加密的有效负载,这不是将来的证明.此外,执行NSDictionary > NSData > NSString转换序列只是为了您可以使用KeychainItemWrapper效率低下:无论如何KeychainItemWrapper都会将您的字符串转换回来NSData加密它.

这是一个完整的解决方案,通过直接利用钥匙串库解决了上述问题.它被实现为一个类别,因此您可以像这样使用它:

// to store your dictionary
[myDict storeToKeychainWithKey:@"myStorageKey"];

// to retrieve it
NSDictionary *myDict = [NSDictionary dictionaryFromKeychainWithKey:@"myStorageKey"];

// to delete it
[myDict deleteFromKeychainWithKey:@"myStorageKey"];
Run Code Online (Sandbox Code Playgroud)


这是类别:

@implementation NSDictionary (Keychain)

-(void) storeToKeychainWithKey:(NSString *)aKey {
    // serialize dict
    NSString *error;
    NSData *serializedDictionary = [NSPropertyListSerialization dataFromPropertyList:self format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];

    // encrypt in keychain
    if(!error) {
        // first, delete potential existing entries with this key (it won't auto update)
        [self deleteFromKeychainWithKey:aKey];

        // setup keychain storage properties
        NSDictionary *storageQuery = @{
            (id)kSecAttrAccount:    aKey,
            (id)kSecValueData:      serializedDictionary,
            (id)kSecClass:          (id)kSecClassGenericPassword,
            (id)kSecAttrAccessible: (id)kSecAttrAccessibleWhenUnlocked
        };
        OSStatus osStatus = SecItemAdd((CFDictionaryRef)storageQuery, nil);
        if(osStatus != noErr) {
            // do someting with error
        }
    }
}


+(NSDictionary *) dictionaryFromKeychainWithKey:(NSString *)aKey {
    // setup keychain query properties
    NSDictionary *readQuery = @{
        (id)kSecAttrAccount: aKey,
        (id)kSecReturnData: (id)kCFBooleanTrue,
        (id)kSecClass:      (id)kSecClassGenericPassword
    };

    NSData *serializedDictionary = nil;
    OSStatus osStatus = SecItemCopyMatching((CFDictionaryRef)readQuery, (CFTypeRef *)&serializedDictionary);
    if(osStatus == noErr) {
        // deserialize dictionary
        NSString *error;
        NSDictionary *storedDictionary = [NSPropertyListSerialization propertyListFromData:serializedDictionary mutabilityOption:NSPropertyListImmutable format:nil errorDescription:&error];
        if(error) {
            NSLog(@"%@", error);
        }
        return storedDictionary;
    }
    else {
        // do something with error
        return nil;
    }
}


-(void) deleteFromKeychainWithKey:(NSString *)aKey {
    // setup keychain query properties
    NSDictionary *deletableItemsQuery = @{
        (id)kSecAttrAccount:        aKey,
        (id)kSecClass:              (id)kSecClassGenericPassword,
        (id)kSecMatchLimit:         (id)kSecMatchLimitAll,
        (id)kSecReturnAttributes:   (id)kCFBooleanTrue
    };

    NSArray *itemList = nil;
    OSStatus osStatus = SecItemCopyMatching((CFDictionaryRef)deletableItemsQuery, (CFTypeRef *)&itemList);
    // each item in the array is a dictionary
    for (NSDictionary *item in itemList) {
        NSMutableDictionary *deleteQuery = [item mutableCopy];
        [deleteQuery setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
        // do delete
        osStatus = SecItemDelete((CFDictionaryRef)deleteQuery);
        if(osStatus != noErr) {
            // do something with error
        }
        [deleteQuery release];
    }
}


@end
Run Code Online (Sandbox Code Playgroud)

实际上,您可以轻松地修改它以在钥匙串中存储任何类型的可序列化对象,而不仅仅是字典.只需要NSData表示要存储的对象.


amo*_*l-c 13

对Dts类别做了一些小改动.转换为ARC并使用NSKeyedArchiver存储自定义对象.

@implementation NSDictionary (Keychain)

-(void) storeToKeychainWithKey:(NSString *)aKey {
    // serialize dict
    NSData *serializedDictionary = [NSKeyedArchiver archivedDataWithRootObject:self];
    // encrypt in keychain
        // first, delete potential existing entries with this key (it won't auto update)
        [self deleteFromKeychainWithKey:aKey];

        // setup keychain storage properties
        NSDictionary *storageQuery = @{
                                       (__bridge id)kSecAttrAccount:    aKey,
                                       (__bridge id)kSecValueData:      serializedDictionary,
                                       (__bridge id)kSecClass:          (__bridge id)kSecClassGenericPassword,
                                       (__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
                                       };
        OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef)storageQuery, nil);
        if(osStatus != noErr) {
            // do someting with error
        }
}


+(NSDictionary *) dictionaryFromKeychainWithKey:(NSString *)aKey {
    // setup keychain query properties
    NSDictionary *readQuery = @{
                                (__bridge id)kSecAttrAccount: aKey,
                                (__bridge id)kSecReturnData: (id)kCFBooleanTrue,
                                (__bridge id)kSecClass:      (__bridge id)kSecClassGenericPassword
                                };

    CFDataRef serializedDictionary = NULL;
    OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)readQuery, (CFTypeRef *)&serializedDictionary);
    if(osStatus == noErr) {
        // deserialize dictionary
        NSData *data = (__bridge NSData *)serializedDictionary;
        NSDictionary *storedDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        return storedDictionary;
    }
    else {
        // do something with error
        return nil;
    }
}


-(void) deleteFromKeychainWithKey:(NSString *)aKey {
    // setup keychain query properties
    NSDictionary *deletableItemsQuery = @{
                                          (__bridge id)kSecAttrAccount:        aKey,
                                          (__bridge id)kSecClass:              (__bridge id)kSecClassGenericPassword,
                                          (__bridge id)kSecMatchLimit:         (__bridge id)kSecMatchLimitAll,
                                          (__bridge id)kSecReturnAttributes:   (id)kCFBooleanTrue
                                          };

    CFArrayRef itemList = nil;
    OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)deletableItemsQuery, (CFTypeRef *)&itemList);
    // each item in the array is a dictionary
    NSArray *itemListArray = (__bridge NSArray *)itemList;
    for (NSDictionary *item in itemListArray) {
        NSMutableDictionary *deleteQuery = [item mutableCopy];
        [deleteQuery setValue:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
        // do delete
        osStatus = SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
        if(osStatus != noErr) {
            // do something with error
        }
    }
}

@end
Run Code Online (Sandbox Code Playgroud)


mal*_*ois 6

编码:[dic description]
解码:[dic propertyList]