博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS-设备唯一标识符
阅读量:5826 次
发布时间:2019-06-18

本文共 7153 字,大约阅读时间需要 23 分钟。

hot3.png

前言

获取设备唯一标识符,开发者大脑里产生的第一方案就是获取设备MAC地址,遗憾的是苹果iOS7之后获取到的mac是固定值,因此在通过MAC地址充当唯一标识此路不通;苹果iOS6另外一个新的方法(IDFA),提供了一个方法advertisingIdentifier,通过调用该方法会返回一个NSUUID实例,最后可以获得一个UUID,由系统存储着的,由于IDFA会出现取不到的情况,故绝不可以作为设备唯一标识符。还有IDFV(identifierForVendor),应用提供商标识符,如果用户将属于此Vender的所有App卸载,则IDFV的值会被重置,即再重装此Vender的App,IDFV的值和之前不同,因此想通过IDFV作为设备唯一标识符,需要KeyChain配合(KeyChain的优缺点)本文不阐述。

 

1、结构设计方案(KeyChain+IDFV)

(1)创建一个共用的KeyChain(钥匙串)的管理对象,负责对KeyChain中Item的增删改查以及Item是否App之间共享等等;

(2)然后根据需求二次封装,不管你要保存UUID还是账户密码,你需要做的就是创建特定的广利对象(比如说下文的XPQKeychainUUID用于管理UUID)封装相应的存储逻辑,最后通过XPQKeychainManager写入KeyChain。

#import 
@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

 

2、保存与获取IDFV,关键代码NSString *uuid =  

[[UIDevice currentDevice].identifierForVendor UUIDString];

#import 
#import
@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;@endstatic 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

 

总结

Keychain内部的数据会自动加密。如果设备没有越狱并且不暴力破解,keychain确实很安全。但是越狱后的设备,keychain就很危险了,因此还需要对重要信息进行加密处理再放入Keychain。

转载于:https://my.oschina.net/u/1450995/blog/907930

你可能感兴趣的文章
zabbix 批量web url监控
查看>>
MongoDB CookBook读书笔记之导入导出
查看>>
shell如何快速锁定所有账号
查看>>
HTML 5实现的手机摇一摇
查看>>
Linux 文件IO理解
查看>>
Ninject 2.x细说---2.绑定和作用域
查看>>
30个非常时尚的网页联系表单设计优秀示例
查看>>
使用membership(System.Web.Security)来进行角色与权限管理
查看>>
opticom 语音质量验证白皮书
查看>>
3D实时渲染中的BSP树和多边形剔除
查看>>
Frank Klemm's Dither and Noise Shaping Page: Dither and Noise Shaping In MPC/MP+
查看>>
网络抓包的部署和工具Wireshark【图书节选】
查看>>
Redis在Windows+linux平台下的安装配置
查看>>
Maven入门实战笔记-11节[6]
查看>>
Local declaration of 'content' hides instance variable
查看>>
ASP.NET中 HTML标签总结及使用
查看>>
Linux下日志系统的设计
查看>>
爬虫IP被禁的简单解决方法——切换UserAgent
查看>>
php生成word,并下载
查看>>
紫书 习题8-11 UVa 1615 (区间选点问题)
查看>>