可以使用KeychainItemWrapper
(或不使用)在iPhone
鑰匙串中存儲NSDictionary
? 如果這是不可能的,你有另一種解決方案?將NSDictionary存儲在鑰匙串中
回答
你必須正確地將其存儲到鑰匙串之前序列化NSDictionary
。 使用:
[dic description]
[dic propertyList]
,你會最終有一個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);
}
第二調用NSPropertyListSerialization
將NSDictionary
集合中保持原始數據類型返回的NSDictionary
。
我編輯了代碼,以更準確地反映如何使用KeychainItemWrapper。 – 2012-11-16 01:24:59
這將數據存儲在不是加密字段的'kSecAttrService'中。我相信你打算在這裏使用'kSecValueData',這是加密的有效載荷。 – 2013-04-23 16:57:19
由於某些原因,您的代碼在ios7中不起作用。會考慮更新它更清楚。例如,你說我們需要使用[dic description],但在你的例子中沒有dic變量。 – user798719 2013-09-02 19:04:58
我發現鑰匙串包裝只需要字符串。甚至沒有NSData。因此,要存儲一個字典,您必須按照Bret的建議來做,但需要額外的步驟將NSData序列化轉換爲字符串。就像這樣:
NSString *error;
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:MY_STRING accessGroup:nil];
NSData *dictionaryRep = [NSPropertyListSerialization dataFromPropertyList:dictToSave format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
NSString *xml = [[NSString alloc] initWithBytes:[dictionaryRep bytes] length:[dictionaryRep length] encoding:NSUTF8StringEncoding];
[keychain setObject:xml forKey:(__bridge id)(kSecValueData)];
回讀:
NSError *error;
NSString *xml = [keychain objectForKey:(__bridge id)(kSecValueData)];
if (xml && xml.length) {
NSData *dictionaryRep = [xml dataUsingEncoding:NSUTF8StringEncoding];
dict = [NSPropertyListSerialization propertyListWithData:dictionaryRep options:NSPropertyListImmutable format:nil error:&error];
if (error) {
NSLog(@"%@", error);
}
}
並非所有的數據都是有效的UTF-8,所以這是行不通的。最好的選擇是編碼到Base64。 – zaph 2015-01-09 04:02:27
它可能工作;畢竟XML是通過聲明UTF-8編碼開始的,<?xml version =「1.0」encoding =「UTF-8」?>。我相信Apple在XML中將數據編碼爲Base64(請參閱https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/PropertyLists/SerializePlist/SerializePlist.html中的示例)。如果確實失敗了,那麼回退到Base64是個好主意。 – 2015-01-09 15:49:46
使用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"];
和這裏的類別:
@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
事實上,你可以輕鬆地修改它來存儲任何類型的序列化對象的,所以你像這樣使用它作爲一個類別實施鑰匙串,而不僅僅是一本字典。只需製作要存儲的對象的NSData
表示形式。
對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
我加訪問組的支持和模擬器安全Amols解決方案:
//
// NSDictionary+SharedKeyChain.h
// LHSharedKeyChain
//
#import <Foundation/Foundation.h>
@interface NSDictionary (SharedKeyChain)
/**
* Returns a previously stored dictionary from the KeyChain.
*
* @param key NSString The name of the dictionary. There can be multiple dictionaries stored in the KeyChain.
* @param accessGroup NSString Access group for shared KeyChains, set to nil for no group.
*
* @return NSDictionary A dictionary that has been stored in the Keychain, nil if no dictionary for the key and accessGroup exist.
*/
+ (NSDictionary *)dictionaryFromKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
/**
* Deletes a previously stored dictionary from the KeyChain.
*
* @param key NSString The name of the dictionary. There can be multiple dictionaries stored in the KeyChain.
* @param accessGroup NSString Access group for shared KeyChains, set to nil for no group.
*/
+ (void)deleteFromKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
/**
* Save dictionary instance to the KeyChain. Any previously existing data with the same key and accessGroup will be overwritten.
*
* @param key NSString The name of the dictionary. There can be multiple dictionaries stored in the KeyChain.
* @param accessGroup NSString Access group for shared KeyChains, set to nil for no group.
*/
- (void)storeToKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
@end
//
// NSDictionary+SharedKeyChain.m
// LHSharedKeyChain
//
#import "NSDictionary+SharedKeyChain.h"
@implementation NSDictionary (SharedKeyChain)
- (void)storeToKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
{
// serialize dict
NSData *serializedDictionary = [NSKeyedArchiver archivedDataWithRootObject:self];
// encrypt in keychain
// first, delete potential existing entries with this key (it won't auto update)
[NSDictionary deleteFromKeychainWithKey:key accessGroup:accessGroup];
// setup keychain storage properties
NSDictionary *storageQuery = @{
(__bridge id)kSecAttrAccount: key,
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
(__bridge id)kSecAttrAccessGroup: accessGroup,
#endif
(__bridge id)kSecValueData: serializedDictionary,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
};
OSStatus status = SecItemAdd ((__bridge CFDictionaryRef)storageQuery, nil);
if (status != noErr)
{
NSLog (@"%d %@", (int)status, @"Couldn't save to Keychain.");
}
}
+ (NSDictionary *)dictionaryFromKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
{
// setup keychain query properties
NSDictionary *readQuery = @{
(__bridge id)kSecAttrAccount: key,
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
(__bridge id)kSecAttrAccessGroup: accessGroup,
#endif
(__bridge id)kSecReturnData: (id)kCFBooleanTrue,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword
};
CFDataRef serializedDictionary = NULL;
OSStatus status = SecItemCopyMatching ((__bridge CFDictionaryRef)readQuery, (CFTypeRef *)&serializedDictionary);
if (status == noErr)
{
// deserialize dictionary
NSData *data = (__bridge NSData *)serializedDictionary;
NSDictionary *storedDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return storedDictionary;
}
else
{
NSLog (@"%d %@", (int)status, @"Couldn't read from Keychain.");
return nil;
}
}
+ (void)deleteFromKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
{
// setup keychain query properties
NSDictionary *deletableItemsQuery = @{
(__bridge id)kSecAttrAccount: key,
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
(__bridge id)kSecAttrAccessGroup: accessGroup,
#endif
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll,
(__bridge id)kSecReturnAttributes: (id)kCFBooleanTrue
};
CFArrayRef itemList = nil;
OSStatus status = 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
status = SecItemDelete ((__bridge CFDictionaryRef)deleteQuery);
if (status != noErr)
{
NSLog (@"%d %@", (int)status, @"Couldn't delete from Keychain.");
}
}
}
@end
- 1. 將NSUserDefault存儲在鑰匙串中
- 2. 在IOS中的鑰匙串包裝中存儲NSArray或NSDictionary
- 3. 在鑰匙串中存儲數據
- 4. iPhone SDK 4.0:從存儲在鑰匙串
- 5. 將證書鏈存儲在應用程序鑰匙串中
- 6. 安全地將數據存儲在鑰匙串中?
- 7. Swift + Locksmith:沒有存儲鑰匙串值
- 8. 鑰匙串iOS不總是存儲值
- 9. iOS中的鑰匙串訪問:「鑰匙串無法將代碼存儲在代碼中:-50」
- 10. 鑰匙串訪問中沒有鑰匙
- 11. 將私鑰導入鑰匙串在iphone
- 12. 如何在鑰匙串中保存CFUUID
- 13. 使用鎖匠在鑰匙串中存儲多個值
- 14. 在鑰匙串中存儲一個的.p12證書使用後
- 15. 在鑰匙串中存儲遊戲狀態和分數
- 16. 在iPhone的鑰匙串中存儲和訪問x509證書
- 17. 在iOS鑰匙串中存儲多個帳戶憑證
- 18. 如何在ios鑰匙串中手動存儲?
- 19. 無法存儲在鑰匙串中值正確
- 20. 使用iOS的鑰匙串來存儲密鑰和API調用
- 21. 如何從鑰匙串中刪除nsdictionary替換
- 22. 以編程方式在OS X鑰匙串中存儲對稱密鑰
- 23. 我應該使用哪個密鑰在iOS鑰匙串中存儲密碼?
- 24. 將RSA密鑰導入iPhone鑰匙串?
- 25. 將私鑰添加到iOS鑰匙串
- 26. 存儲和檢索IOS鑰匙串中的用戶數據
- 27. 在iOS上存儲身份驗證令牌 - NSUserDefaults與鑰匙串?
- 28. 如何使用iOS鑰匙串在iPhone上存儲OAuth令牌?
- 29. Cheapshark API:閱讀NSDictionary的無鑰匙
- 30. 如何將RSA publicKey/privateKey保存在鑰匙串中
是的,但是當我讀到的數據,我有一個空的NSString參考。 – malinois 2012-03-30 18:51:25