NSString+Keychain.m 6.52 KB
//
//  NSString+Keychain.m
//  XWTest
//
//  Created by CNLive-zxw on 2019/7/8.
//  Copyright © 2019 CNLive. All rights reserved.
//

#import "NSString+Keychain.h"
#import <Security/Security.h>

NSString * const CNUSER_KEY_UUID_INSTEAD = @"com.cnlive.CNLiveNetAdd.uuid";
NSString * const CNUSER_KEY_TIME_INSTEAD = @"com.cnlive.CNLiveNetAdd.time";

@implementation NSString (Keychain)
/**
* 保存已有的UUID_TS到钥匙串
*
* @param UUID UUID的字符串
* @param TS TS的字符串
*/
+ (void)xw_saveUUID:(NSString *)UUID TS:(NSString *)TS {
    if (!XW_UUID_Exist || !XW_Timestamp_Exist) {
        [NSString xw_save:CNUSER_KEY_UUID_INSTEAD data:UUID];
        [NSString xw_save:CNUSER_KEY_TIME_INSTEAD data:TS];
    }
}
/**
 * @abstract  获取设备UUID
 *
 * 本方法是得到 UUID 后存入系统中的 keychain 的方法
 * 程序删除后重装,仍可以得到相同的唯一标示
 * 但是当系统刷机后,系统中的钥匙串会被清空,此时本方法失效
 *
 * @return 返回UUID字符串对象
 */
+ (NSString *)xw_getUUIDInKeychain {
    NSString *getUDIDInKeychain = (NSString *)[NSString xw_load:CNUSER_KEY_UUID_INSTEAD];
    NSLog(@"最终 ———— UUID %@",getUDIDInKeychain);
    return getUDIDInKeychain;
    
}

+ (BOOL)xw_existUUID{
    NSString *getUDIDInKeychain = (NSString *)[NSString xw_load:CNUSER_KEY_UUID_INSTEAD];
    NSLog(@"从keychain中获取到的 UUID %@",getUDIDInKeychain);
    if (!getUDIDInKeychain ||[getUDIDInKeychain isEqualToString:@""]||[getUDIDInKeychain isKindOfClass:[NSNull class]]) {
        return NO;
    }else{
        return YES;
        
    }
    
}

/**
 * @abstract  获取时间戳
 * 获取的时间戳就是第一次调用xw_getTimeInKeychain这个方法,此方法会把传入的时间戳存入系统中的 keychain
 * 我们调用此方法获取时间戳的时候,如果有,则返回;如果没有,则返回当前时间戳
 * 一定确保此方法在xw_getTimeInKeychain之后调用 *
 *
 * @return  返回时间戳字符串对象
 */
+ (NSString *)xw_getTimestampInKeychain {
    NSString *getTimeInKeychain = (NSString *)[NSString xw_load:CNUSER_KEY_TIME_INSTEAD];
    return getTimeInKeychain;
    
}

+ (BOOL)xw_existTimestamp{
    NSString *getTimeInKeychain = (NSString *)[NSString xw_load:CNUSER_KEY_TIME_INSTEAD];
    NSLog(@"从keychain中获取到的 UUID %@",getTimeInKeychain);
    if (!getTimeInKeychain ||[getTimeInKeychain isEqualToString:@""]||[getTimeInKeychain isKindOfClass:[NSNull class]]) {
        return NO;
    }else{
        return YES;
        
    }
    
}


/**
 * @abstract 获取UUID和_和时间戳3个的拼接对象
 *
 * 本方法一定要使用时间戳字符串调用 *
 * 本方法是得到 UUID_时间戳 后存入系统中的 keychain 的方法
 * 程序删除后重装,仍可以得到相同的唯一标示
 * 但是当系统刷机后,系统中的钥匙串会被清空,此时本方法失效
 *
 * @return 返回获取UUID和_和时间戳3个的拼接对象
 */
- (NSString *)xw_getUUIDTimestampInKeychain {
    NSString *getUDIDInKeychain = (NSString *)[NSString xw_load:CNUSER_KEY_UUID_INSTEAD];
    NSLog(@"从keychain中获取到的 UUID %@",getUDIDInKeychain);
    if (!getUDIDInKeychain ||[getUDIDInKeychain isEqualToString:@""]||[getUDIDInKeychain isKindOfClass:[NSNull class]]) {
        CFUUIDRef puuid = CFUUIDCreate( nil );
        CFStringRef uuidString = CFUUIDCreateString( nil, puuid );
        NSString * idfv = (NSString *)CFBridgingRelease(CFStringCreateCopy( NULL, uuidString));
        NSString *result = [idfv stringByReplacingOccurrencesOfString:@"-" withString:@""];
        
        CFRelease(puuid);
        CFRelease(uuidString);
        NSLog(@"\n \n \n _____重新存储 UUID_____\n \n \n  %@",result);
        [NSString xw_save:CNUSER_KEY_UUID_INSTEAD data:result];
        getUDIDInKeychain = (NSString *)[NSString xw_load:CNUSER_KEY_UUID_INSTEAD];
    }
    
    NSString *getTimeInKeychain = (NSString *)[NSString xw_load:CNUSER_KEY_TIME_INSTEAD];
    if (!getTimeInKeychain ||[getTimeInKeychain isEqualToString:@""]||[getTimeInKeychain isKindOfClass:[NSNull class]]) {
        [NSString xw_save:CNUSER_KEY_TIME_INSTEAD data:self];
        getTimeInKeychain = (NSString *)[NSString xw_load:CNUSER_KEY_TIME_INSTEAD];
    }
    
    NSString *string = [NSString stringWithFormat:@"%@_%@",getUDIDInKeychain,getTimeInKeychain];
    NSLog(@"最终 ———— UUID_时间戳 %@",string);
    return string;
    
}


//保存数据至钥匙串中
+ (void)xw_save:(NSString *)service data:(id)data {
    //Get search dictionary
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    //Delete old item before add new item
    SecItemDelete((CFDictionaryRef)keychainQuery);
    //Add new object to search dictionary(Attention:the data format)
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    //Add item to keychain with the search dictionary
    SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
    
}


//从钥匙串中加载数据
+ (id)xw_load:(NSString *)service {
    id ret = nil;
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    //Configure the search setting
    //Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    CFDataRef keyData = NULL;
    if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
        @try {
            ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
        } @catch (NSException *e) {
            NSLog(@"Unarchive of %@ failed: %@", service, e);
        } @finally {
        }
    }
    if (keyData)
        CFRelease(keyData);
    return ret;
    
}


//删除数据
+ (void)xw_delete:(NSString *)service {
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    SecItemDelete((CFDictionaryRef)keychainQuery);
    
}


//通过 key 获取数据
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
    return [NSMutableDictionary dictionaryWithObjectsAndKeys:
            (id)kSecClassGenericPassword,(id)kSecClass,
            service, (id)kSecAttrService,
            service, (id)kSecAttrAccount,
            (id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
            nil];
}

+ (void)xw_deletes{
    [NSString xw_delete:CNUSER_KEY_UUID_INSTEAD];
    [NSString xw_delete:CNUSER_KEY_TIME_INSTEAD];

}

@end