前言安全
获取设备惟一标识符,开发者大脑里产生的第一方案就是获取设备MAC地址,遗憾的是苹果iOS7以后获取到的mac是固定值,所以在经过MAC地址充当惟一标识此路不通;苹果iOS6另一个新的方法(IDFA),提供了一个方法advertisingIdentifier,经过调用该方法会返回一个NSUUID实例,最后能够得到一个UUID,由系统存储着的,因为IDFA会出现取不到的状况,故毫不能够做为设备惟一标识符。还有IDFV(identifierForVendor),应用提供商标识符,若是用户将属于此Vender的全部App卸载,则IDFV的值会被重置,即再重装此Vender的App,IDFV的值和以前不一样,所以想经过IDFV做为设备惟一标识符,须要KeyChain配合(KeyChain的优缺点)本文不阐述。ide
一、结构设计方案(KeyChain+IDFV)ui
(1)建立一个共用的KeyChain(钥匙串)的管理对象,负责对KeyChain中Item的增删改查以及Item是否App之间共享等等;atom
(2)而后根据需求二次封装,无论你要保存UUID仍是帐户密码,你须要作的就是建立特定的广利对象(好比说下文的XPQKeychainUUID用于管理UUID)封装相应的存储逻辑,最后经过XPQKeychainManager写入KeyChain。加密
#import <Foundation/Foundation.h> @interface XPQKeychainManager : NSObject - (instancetype)initSecAttrAccessGroup:(NSString *)secAttrAccessGroup; - (id)secValueDataForService:(NSString *)aService; - (id)secValueDataForService:(NSString *)aService account:(NSString *)account; - (BOOL)saveKeychainData:(id)data service:(NSString *)aService; - (BOOL)saveKeychainData:(id)data service:(NSString *)aService account:(NSString *)account; - (BOOL)deleteKeychainDataForService:(NSString *)aService; - (BOOL)deleteKeychainDataForService:(NSString *)aService account:(NSString *)account; @end #import "XPQKeychainManager.h" @interface XPQKeychainManager () @property (nonatomic, copy) NSString *secAttrAccessGroup; @end @implementation XPQKeychainManager - (instancetype)init { return [self initSecAttrAccessGroup:nil]; } - (instancetype)initSecAttrAccessGroup:(NSString *)secAttrAccessGroup { self = [super init]; if (self) { self.secAttrAccessGroup = secAttrAccessGroup; } return self; } - (NSMutableDictionary *)queryKeychainForService:(NSString *)aService account:(NSString *)account { return [NSMutableDictionary dictionaryWithObjectsAndKeys: (__bridge id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass, aService, (__bridge_transfer id)kSecAttrService, account, (__bridge_transfer id)kSecAttrAccount, (__bridge id)kSecAttrAccessibleAfterFirstUnlock,(__bridge_transfer id)kSecAttrAccessible, nil]; } - (id)secValueDataForService:(NSString *)aService { return [self secValueDataForService:aService account:aService]; } - (id)secValueDataForService:(NSString *)aService account:(NSString *)account { if (!aService && !account) { return nil; } NSMutableDictionary *keychainQuery = [self queryKeychainForService:aService account:account]; if (self.secAttrAccessGroup) { [keychainQuery setObject:self.secAttrAccessGroup forKey:(__bridge_transfer id)kSecAttrAccessGroup]; } NSMutableDictionary *attributeQuery = [keychainQuery mutableCopy]; [attributeQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnAttributes]; CFTypeRef attrResult = NULL; OSStatus status = SecItemCopyMatching(((__bridge_retained CFDictionaryRef)attributeQuery), (CFTypeRef *)&attrResult); if (status != noErr) { return nil; } NSMutableDictionary *secValueDataQuery = [keychainQuery mutableCopy]; [secValueDataQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData]; CFTypeRef resData = NULL; status = SecItemCopyMatching(((__bridge_retained CFDictionaryRef)secValueDataQuery), (CFTypeRef *)&resData); NSData *resultData = (__bridge_transfer NSData *)resData; if (status != noErr) { return nil; } return resultData ? [NSKeyedUnarchiver unarchiveObjectWithData:resultData] : nil; } - (BOOL)saveKeychainData:(id)data service:(NSString *)aService { return [self saveKeychainData:data service:aService account:aService]; } - (BOOL)saveKeychainData:(id)data service:(NSString *)aService account:(NSString *)account { if (!aService && !account) { return NO; } NSMutableDictionary *keychainQuery = [self queryKeychainForService:aService account:account]; if (self.secAttrAccessGroup) { [keychainQuery setObject:self.secAttrAccessGroup forKey:(__bridge_transfer id)kSecAttrAccessGroup]; } id keychainData = [self secValueDataForService:aService account:account]; OSStatus status = noErr; if (keychainData) { NSMutableDictionary *updateKeychainData = [keychainQuery mutableCopy]; if (![keychainData isEqualToString:data]) { NSMutableDictionary *tempCheck = [NSMutableDictionary dictionaryWithObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData]; status = SecItemUpdate(((__bridge_retained CFDictionaryRef)updateKeychainData), (__bridge_retained CFDictionaryRef)tempCheck); } } else { NSMutableDictionary *addKeychainData = [keychainQuery mutableCopy]; [addKeychainData setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData]; status = SecItemAdd(((__bridge_retained CFDictionaryRef)addKeychainData), NULL); } if (status != noErr) { return NO; } return YES; } - (BOOL)deleteKeychainDataForService:(NSString *)aService { return [self deleteKeychainDataForService:aService account:aService]; } - (BOOL)deleteKeychainDataForService:(NSString *)aService account:(NSString *)account { if (!aService && !account) { return NO; } NSMutableDictionary *keychainQuery = [self queryKeychainForService:aService account:account]; OSStatus status = SecItemDelete(((__bridge_retained CFDictionaryRef)keychainQuery)); if (status != noErr) { return NO; } return YES; } @end
二、保存与获取IDFV,关键代码NSString *uuid =
[[UIDevice currentDevice].identifierForVendor UUIDString];设计
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface XPQKeychainUUID : NSObject - (id)readUUID; - (BOOL)deleteItem; @end #import "XPQKeychainUUID.h" #import "XPQKeychainManager.h" @interface XPQKeychainUUID () @property (nonatomic, copy) NSString *uuid; @property (nonatomic, strong) XPQKeychainManager *keychainManager; @end static NSString * const KEY_IN_KEYCHAIN_UUID = @"com.xpq.keychain.uuid"; @implementation XPQKeychainUUID - (instancetype)init { self = [super init]; if (self) { self.keychainManager = [[XPQKeychainManager alloc] init]; } return self; } - (BOOL)save:(id)data service:(NSString *)service { return [self.keychainManager saveKeychainData:data service:KEY_IN_KEYCHAIN_UUID]; } - (NSString *)getUUID { return [[UIDevice currentDevice].identifierForVendor UUIDString]; } - (id)readUUID { if (!_uuid || _uuid.length == 0) { NSString *strUUID = [self load:KEY_IN_KEYCHAIN_UUID]; if (!strUUID || strUUID.length == 0) { strUUID = [self getUUID]; [self save:strUUID]; } _uuid = strUUID; } return _uuid; } - (id)load:(NSString *)service { return [self.keychainManager secValueDataForService:KEY_IN_KEYCHAIN_UUID]; } - (BOOL)save:(id)data { return [self save:data service:KEY_IN_KEYCHAIN_UUID]; } - (BOOL)deleteItem { return [self.keychainManager deleteKeychainDataForService:KEY_IN_KEYCHAIN_UUID]; } @end
总结code
Keychain内部的数据会自动加密。若是设备没有越狱而且不暴力破解,keychain确实很安全。可是越狱后的设备,keychain就很危险了,所以还须要对重要信息进行加密处理再放入Keychain。对象