iOS获取设备卸载后不变的UUID

Posted qigemingnan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS获取设备卸载后不变的UUID相关的知识,希望对你有一定的参考价值。

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的主要内容,如果未能解决你的问题,请参考以下文章

获取iOS设备UUID

iOS开发-获取设备唯一标识符(UUID)

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

iOS编程——经过UUID和KeyChain来代替Mac地址实现iOS设备的唯一标示(OC版)

IOS - - 获取设备的UUID

离子+电容|在 iOS 设备中获取设备 UUID