iOS-設備惟一標識符

前言安全

獲取設備惟一標識符,開發者大腦裏產生的第一方案就是獲取設備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。對象

相關文章
相關標籤/搜索