iOS 用keychain鑰匙串保存賬號、設備UUID及APP間共享

iOS的keychain服務提供了一種安全的保存私密信息(密碼,序列號,證書等)的方式,每個ios程序都有一個獨立的keychain存儲。相對于NSUserDefaults、文件保存等一般方式,keychain保存更為安全,而且keychain里保存的信息不會因App被刪除而丟失。

  1. 方法:增刪查改
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,添加節點keykeychain-access-groupsarray,如下圖:

keychain-plist.png

$(AppIdentifierPrefix)為開發者帳號對應的ID;

B.在APP target的bulibSetting里面設置Code Signing Entitlements,指向包含AceessGroup的分組信息的plist文件,如下圖(這時plist需在最頂層文件夾中):


codesigning.png

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

參考

https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/index.html#//apple_ref/doc/uid/TP30000898

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • github排名https://github.com/trending,github搜索:https://gith...
    小米君的demo閱讀 4,792評論 2 38
  • WebSocket-Swift Starscream的使用 WebSocket 是 HTML5 一種新的協議。它實...
    香橙柚子閱讀 24,065評論 8 183
  • 我把 “愿得一人心 白首不相離” 這份祈愿 刻在了石頭上 卻獨獨忘了 刻在 你心間 如今 石上祈愿歷歷在目 而你我...
    金城丁香開閱讀 390評論 12 16
  • 大家好,我叫趙梓言也叫趙添添,今天我從暖氣管上摔下來了,在老師的鼓勵下我堅持了跳舞,小朋友們還關心我,我很開心。我...
    添添的日記閱讀 257評論 1 2
  • 我懷疑我的前世是一條魚 不然我的內心為什么會有那么多的水兒在晃動 不然為什么我的內心可以嚼著淚滴般的露珠 我的前世...
    落英冰封閱讀 1,138評論 0 9