1.首先导入系统库Security.framework

2.创建文件SFHFKeychainUtils.h如下(复制即可):

@interface SFHFKeychainUtils : NSObject {

}

+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;

+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error;

+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;

@end

SFHFKeychainUtils.m如下

#import "SFHFKeychainUtils.h"

#import

static NSString *SFHFKeychainUtilsErrorDomain = @"SFHFKeychainUtilsErrorDomain";

#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR

@interface SFHFKeychainUtils (PrivateMethods)

+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;

@end

#endif

@implementation SFHFKeychainUtils

#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR

+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {

if (!username || !serviceName) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

return nil;

}

SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];

if (*error || !item) {

return nil;

}

// from Advanced Mac OS X Programming, ch. 16

UInt32 length;

char *password;

SecKeychainAttribute attributes[8];

SecKeychainAttributeList list;

attributes[0].tag = kSecAccountItemAttr;

attributes[1].tag = kSecDescriptionItemAttr;

attributes[2].tag = kSecLabelItemAttr;

attributes[3].tag = kSecModDateItemAttr;

list.count = 4;

list.attr = attributes;

OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password);

if (status != noErr) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

return nil;

}

NSString *passwordString = nil;

if (password != NULL) {

char passwordBuffer[1024];

if (length > 1023) {

length = 1023;

}

strncpy(passwordBuffer, password, length);

passwordBuffer[length] = '\0';

passwordString = [NSString stringWithCString:passwordBuffer];

}

SecKeychainItemFreeContent(&list, password);

CFRelease(item);

return passwordString;

}

+ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error {

if (!username || !password || !serviceName) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

return;

}

OSStatus status = noErr;

SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];

if (*error && [*error code] != noErr) {

return;

}

*error = nil;

if (item) {

status = SecKeychainItemModifyAttributesAndData(item,

NULL,

strlen([password UTF8String]),

[password UTF8String]);

CFRelease(item);

}

else {

status = SecKeychainAddGenericPassword(NULL,

strlen([serviceName UTF8String]),

[serviceName UTF8String],

strlen([username UTF8String]),

[username UTF8String],

strlen([password UTF8String]),

[password UTF8String],

NULL);

}

if (status != noErr) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

}

+ (void) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {

if (!username || !serviceName) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: 2000 userInfo: nil];

return;

}

*error = nil;

SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];

if (*error && [*error code] != noErr) {

return;

}

OSStatus status;

if (item) {

status = SecKeychainItemDelete(item);

CFRelease(item);

}

if (status != noErr) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

}

+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {

if (!username || !serviceName) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

return nil;

}

*error = nil;

SecKeychainItemRef item;

OSStatus status = SecKeychainFindGenericPassword(NULL,

strlen([serviceName UTF8String]),

[serviceName UTF8String],

strlen([username UTF8String]),

[username UTF8String],

NULL,

NULL,

&item);

if (status != noErr) {

if (status != errSecItemNotFound) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

return nil;

}

return item;

}

#else

+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {

if (!username || !serviceName) {

if (error != nil) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

}

return nil;

}

if (error != nil) {

*error = nil;

}

// Set up a query dictionary with the base query attributes: item type (generic), username, and service

NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount,kSecAttrService, nil] autorelease];

NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, nil] autorelease];

NSMutableDictionary *query = [[[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];

// First do a query for attributes, in case we already have a Keychain item with no password data set.

// One likely way such an incorrect item could have come about is due to the previous (incorrect)

// version of this code (which set the password as a generic attribute instead of password data).

NSDictionary *attributeResult = NULL;

NSMutableDictionary *attributeQuery = [query mutableCopy];

[attributeQuery setObject: (id) kCFBooleanTrue forKey:(id) kSecReturnAttributes];

OSStatus status = SecItemCopyMatching((CFDictionaryRef) attributeQuery, (CFTypeRef *) &attributeResult);

[attributeResult release];

[attributeQuery release];

if (status != noErr) {

// No existing item found--simply return nil for the password

if (error != nil && status != errSecItemNotFound) {

//Only return an error if a real exception happened--not simply for "not found."

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

return nil;

}

// We have an existing item, now query for the password data associated with it.

NSData *resultData = nil;

NSMutableDictionary *passwordQuery = [query mutableCopy];

[passwordQuery setObject: (id) kCFBooleanTrue forKey: (id) kSecReturnData];

status = SecItemCopyMatching((CFDictionaryRef) passwordQuery, (CFTypeRef *) &resultData);

[resultData autorelease];

[passwordQuery release];

if (status != noErr) {

if (status == errSecItemNotFound) {

// We found attributes for the item previously, but no password now, so return a special error.

// Users of this API will probably want to detect this error and prompt the user to

// re-enter their credentials.  When you attempt to store the re-entered credentials

// using storeUsername:andPassword:forServiceName:updateExisting:error

// the old, incorrect entry will be deleted and a new one with a properly encrypted

// password will be added.

if (error != nil) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo:nil];

}

}

else {

// Something else went wrong. Simply return the normal Keychain API error code.

if (error != nil) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo:nil];

}

}

return nil;

}

NSString *password = nil;

if (resultData) {

password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding];

}

else {

// There is an existing item, but we weren't able to get password data for it for some reason,

// Possibly as a result of an item being incorrectly entered by the previous code.

// Set the -1999 error so the code above us can prompt the user again.

if (error != nil) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];

}

}

return [password autorelease];

}

+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error

{

if (!username || !password || !serviceName)

{

if (error != nil)

{

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

}

return NO;

}

// See if we already have a password entered for these credentials.

NSError *getError = nil;

NSString *existingPassword = [SFHFKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError];

if ([getError code] == -1999)

{

// There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code.

// Delete the existing item before moving on entering a correct one.

getError = nil;

[self deleteItemForUsername: username andServiceName: serviceName error: &getError];

if ([getError code] != noErr)

{

if (error != nil)

{

*error = getError;

}

return NO;

}

}

else if ([getError code] != noErr)

{

if (error != nil)

{

*error = getError;

}

return NO;

}

if (error != nil)

{

*error = nil;

}

OSStatus status = noErr;

if (existingPassword)

{

// We have an existing, properly entered item with a password.

// Update the existing item.

if (![existingPassword isEqualToString:password] && updateExisting)

{

//Only update if we're allowed to update existing.  If not, simply do nothing.

NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass,

kSecAttrService,

kSecAttrLabel,

kSecAttrAccount,

nil] autorelease];

NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword,

serviceName,

serviceName,

username,

nil] autorelease];

NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];

status = SecItemUpdate((CFDictionaryRef) query, (CFDictionaryRef) [NSDictionarydictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (NSString *)kSecValueData]);

}

}

else

{

// No existing entry (or an existing, improperly entered, and therefore now

// deleted, entry).  Create a new entry.

NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass,

kSecAttrService,

kSecAttrLabel,

kSecAttrAccount,

kSecValueData,

nil] autorelease];

NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword,

serviceName,

serviceName,

username,

[password dataUsingEncoding: NSUTF8StringEncoding],

nil] autorelease];

NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];

status = SecItemAdd((CFDictionaryRef) query, NULL);

}

if (status != noErr)

{

// Something went wrong with adding the new item. Return the Keychain error code.

if (error != nil) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

return NO;

}

return YES;

}

+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error

{

if (!username || !serviceName)

{

if (error != nil)

{

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

}

return NO;

}

if (error != nil)

{

*error = nil;

}

NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount,kSecAttrService, kSecReturnAttributes, nil] autorelease];

NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil] autorelease];

NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];

OSStatus status = SecItemDelete((CFDictionaryRef) query);

if (status != noErr)

{

if (error != nil) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

return NO;

}

return YES;

}

#endif

@end

 
这里才是重要的代码
3.在获取uuid类里导入头文件#import"SFHFKeychainUtils.h"
下面是获取方法每次调用此方法即可

+(NSString*) GetIOSUUID

{

NSError *error;

NSString * string = [SFHFKeychainUtils getPasswordForUsername:@"UUID" andServiceName:@"com.china.TestKeyChain" error:&error];

if (!string) {

}

if(error || !string){

NSLog(@"❌从Keychain里获取密码出错:%@", error);

[self saveUUID];//保存

string = [SFHFKeychainUtils getPasswordForUsername:@"UUID" andServiceName:@"com.china.TestKeyChain" error:&error];

}

else{

NSLog(@"✅从Keychain里获取密码成功!密码为%@",string);

}

return string;

}

保存uuid方法(此方法不必自己调用)

+(void)saveUUID

{

CFUUIDRef puuid = CFUUIDCreate( nil );

CFStringRef uuidString = CFUUIDCreateString( nil, puuid );

NSString * result = (NSString *)CFBridgingRelease(CFStringCreateCopy( NULL, uuidString));

CFRelease(puuid);

CFRelease(uuidString);

NSError *error;

BOOL saved = [SFHFKeychainUtils storeUsername:@"UUID" andPassword:result

forServiceName:@"com.china.TestKeyChain" updateExisting:YESerror:&error];

if (!saved) {

NSLog(@"❌Keychain保存密码时出错:%@", error);

}else{

NSLog(@"✅Keychain保存密码成功!%@",result);

}

}

4.最后就要测试了,运行时打印一下uuid,然后卸载了重新运行打印,发现一样就OK了
 
原文  http://blog.sina.com.cn/s/blog_15383d4e60102w5fv.html  谢谢分享

iOS获取设备卸载后不变的UUID的更多相关文章

  1. iOS获取设备唯一标识的8种方法

    8种iOS获取设备唯一标识的方法,希望对大家有用. UDID UDID(Unique Device Identifier),iOS 设备的唯一识别码,是一个40位十六进制序列(越狱的设备通过某些工具可 ...

  2. iOS获取设备型号、装置类型等信息

    iOS获取设备型号.设备类型等信息 设备标识 关于设备标识,历史上盛行过很多英雄,比如UDID.Mac地址.OpenUDID等,然而他们都陆陆续续倒在了苹果的门下.苹果目前提供了2个方法供App获取设 ...

  3. DeviceUuidFactory【获取设备唯一标识码的UUID(加密)】【需要运行时权限的处理的配合】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 有时需要对用户设备进行标识,所以希望能够得到一个稳定可靠并且唯一的识别码.虽然Android系统中提供了这样设备识别码,但是由于An ...

  4. iOS获取设备型号和App版本号等信息(OC+Swift)

    iOS获取设备型号和App版本号等信息(OC+Swift) 字数1687 阅读382 评论3 喜欢10 好久没有写过博客了,因为中间工作比较忙,然后有些个人事情所以耽误了.但是之前写的博客还一直有人来 ...

  5. iOS 获取设备唯一标示符的方法

    在开发中会遇到应用需要记录设备标示,即使应用卸载后再安装也可重新识别的情况,在这写一种实现方式--读取设备的UUID(Universally Unique Identifier)并通过KeyChain ...

  6. iOS获取设备唯一标识的各种方法?IDFA、IDFV、UDID分别是什么含义?

    一.UDID (Unique Device Identifier) UDID的全称是Unique Device Identifier,顾名思义,它就是苹果IOS设备的唯一识别码,它由40个字符的字母和 ...

  7. ios获取设备信息总结

    1.获取设备的信息 UIDevice *device = [[UIDevice alloc] int]; NSString *name = device.name;       //获取设备所有者的名 ...

  8. ios 获取设备相关的信息

    .获取设备的信息 UIDevice *device = [[UIDevice alloc] int]; NSString *name = device.name; //获取设备所有者的名称 NSStr ...

  9. iOS 获取设备的唯一标识

    有时候,我们需要记录一下设备的唯一标识,比如标识这个设备是不是已经发过促销券了或者是否下载试用过app等等.最简单 的方法就是获取设备的UDID#[UIDevice currentDevice] un ...

随机推荐

  1. C语言:使用realloc函数对malloc或者calloc动态分配的内存大小进行扩展

    #include<stdio.h> #include<stdlib.h> #include<time.h> typedef struct { char name[3 ...

  2. 附2 hystrix详述(2)- 配置

    一.hystrix在生产中的建议 1.保持timeout的默认值(1000ms),除非需要修改(其实通常会修改) 2.保持threadpool的的线程数为10个,除非需要更多 3.依赖标准的报警和监控 ...

  3. php解决乱码问题时,出现Cannot modify header information问题的解决方法

    设置页面编码使用: header("Content-Type:text/html;charset=utf-8"); 会出现:Cannot modify header informa ...

  4. Binary Tree Inorder Traversal leetcode java

    题目: Given a binary tree, return the inorder traversal of its nodes' values. For example: Given binar ...

  5. IOS中键盘隐藏几种方式

    在ios开发中,经常需要输入信息.输入信息有两种方式: UITextField和UITextView.信息输入完成后,需要隐藏键盘,下面为大家介绍几种隐藏键盘的方式. <一> 点击键盘上的 ...

  6. 一直出现 Enter passphrase for key '/root/.ssh/gitkey12.pub'

    案例: 我一下没有设置密码的pub key, 一使用就要求: Enter passphrase for key '/root/.ssh/gitkey12.pub',   原因:本该设置私钥的地方,设置 ...

  7. Hive Python Streaming的原理及写法

    在Hive中,须要实现Hive中的函数无法实现的功能时,就能够用Streaming来实现. 其原理能够理解成:用HQL语句之外的语言,如Python.Shell来实现这些功能,同一时候配合HQL语句, ...

  8. linux下线程调试 ulimit core

    在linux 下写线程程序的同学预计都遇到过找bug找到崩溃的情况.多线程情况下bug的追踪实在是不easy. 如今我来介绍一个好用的方法 ulimit core. 先简介一下ulimit是个什么(你 ...

  9. linux驱动杂项

    linux驱动 结构体中的逗号 http://zhouyang340.blog.163.com/blog/static/3024095920123495051607/ 下面我们看一个例子,Linux- ...

  10. 算法笔记_119:蓝桥杯第六届省赛(Java语言A组)试题解答

     目录 1 熊怪吃核桃 2 星系炸弹 3 九数分三组 4 循环节长度 5 打印菱形 6 加法变乘法 7 牌型种数 8 移动距离 9 垒骰子 10 灾后重建   前言:以下试题解答代码部分仅供参考,若有 ...