iOS的keychain服務提供了一種安全的保存私密信息(密碼,序列號,證書等)的方式,每個ios程序都有一個獨立的keychain存儲。相對于NSUserDefaults、文件保存等一般方式,keychain保存更為安全,而且keychain里保存的信息不會因App被刪除而丟失。
- 方法:增刪查改
OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef * __nullable CF_RETURNS_RETAINED result)//增
OSStatus SecItemDelete(CFDictionaryRef query) //刪
OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef * __nullable CF_RETURNS_RETAINED result) //查
OSStatus SecItemUpdate(CFDictionaryRef query,
CFDictionaryRef attributesToUpdate) //改
所有方法的參數設置都需要通過CFDictionaryRef,所以創建一個專門獲得NSDictionary
的方法,然后通過橋接__bridge CFDictionaryRef
的方式獲得參數
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)key forAccessGroup:(NSString *)group{
NSMutableDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService : key,
(__bridge id)kSecAttrAccount : key,
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlock
}.mutableCopy;
if (group != nil) {
[query setObject:[self getFullAccessGroup:group] forKey:(__bridge id)kSecAttrAccessGroup];
}
return query;
}
參數太多,而且在頭文件SecItem.h中有詳細說明,這里就不一一詳說(詳說也我也說不清),比如最后一個參數:
kSecAttrAccessibleAfterFirstUnlock
Item data can only be accessed once the device has been unlocked after a restart.
This is recommended for items that need to be accesible by background applications.
Items with this attribute will migrate to a new device when using encrypted backups.
2.保存方法:
+ (BOOL)setValue:(id)value forKey:(NSString *)key forAccessGroup:(NSString *)group{
NSMutableDictionary *query = [self getKeychainQuery:key forAccessGroup:group];
[self deleteValueForKey:key forAccessGroup:group];
NSData *data = nil;
@try {
data = [NSKeyedArchiver archivedDataWithRootObject:value];
} @catch (NSException *exception) {
NSLog(@"archived failure value %@ %@",value,exception);
return NO;
}
[query setObject:data forKey:(__bridge id)kSecValueData];
OSStatus result = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
return result == errSecSuccess;
}
其中,保存是先刪掉之前的key
,沒有使用update
,感覺這樣簡單;然后保存的value
轉換為NSData
,如果value
為自定義object
,則需遵循NSSecureCoding
協議,實現編碼方法,如下:
@interface YTestObject : NSObject<NSSecureCoding>
@property (strong,nonatomic) NSString *string;
@end
#import "YTestObject.h"
@implementation YTestObject
- (NSString *)description{
return [NSString stringWithFormat:@"<YTestObject:string:%@>",_string];
}
+ (BOOL)supportsSecureCoding{
return YES;
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:_string forKey:@"string"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
_string = [aDecoder decodeObjectForKey:@"string"];
return self;
}
@end
另外,為了防止在保存不支持NSSecureCoding
協議的類時crash,使用了@try
,簡單介紹:
@try {
//執行的代碼,其中可能有異常。一旦發現異常,則立即跳到catch執行。否則不會執行catch里面的內容
} @catch (NSException *exception) {
//除非try里面執行代碼發生了異常,否則這里的代碼不會執行
} @finally {
//不管什么情況都會執行,包括try catch 里面用了return ,可以理解為只要執行了try或者catch,就一定會執行 finally
}
3.keychain-access-groups
keychain允許同一個開發商的多個APP共享指定AccessGroup內的數據。
A.創建plish
文件如KeychainAccessGroups.plist
,添加節點key
為keychain-access-groups
的array
,如下圖:
$(AppIdentifierPrefix)為開發者帳號對應的ID;
B.在APP target的bulibSetting里面設置Code Signing Entitlements,指向包含AceessGroup的分組信息的plist文件,如下圖(這時plist需在最頂層文件夾中):
4.設備UUID
有了上面的方法后,直接創建一個NSUUID
保存即可;
- (NSString *)getUUID{
NSString *uuid = [YKeychain valueForKey:@"YDeviceUUID"];
if (uuid == nil) {
uuid = [NSUUID UUID].UUIDString;
[YKeychain setValue:uuid forKey:@"YDeviceUUID"];
}
return uuid;
}
5.最后
show me the code