zoukankan      html  css  js  c++  java
  • SvUDID实现设备唯一标示

    //SvUDIDTools : https://github.com/smileEvday/SvUDID
    
    //将生成的UDID保存到钥匙串中,用户卸载app再重新安装UDID也不会改变.
    
    
    /* 用法1:(摘于网上的使用方法)
    在工程目录下新建一个KeychainAccessGroups.plist文件,该文件的结构中最顶层的节点必须是一个名为“keychain-access-groups”的Array,
    并且该Array中每一项都是一个描述分组的NSString。yourAppID.com.yourCompany.whatever就是你要起的公共区名称,除了whatever字段可以随便定之外,
    其 他的都必须如实填写。这个文件的路径要配置 在 Project->build setting->Code Signing Entitlements里,否则公共区无效,配置好后, 须用你正式的证书签名编译才可通过,
    否则xcode会弹框告诉你code signing有问题。所以,苹果限制了你只能同公司的产品共享 KeyChain数据,别的公司访问不了你公司产品的KeyChain。
    如果拷贝:则将UID文件夹下面的KeychainAccessGroups.plist拷贝到同级于工程目录,并拖曳到xcode工程窗口修改响应的yourAppID.com.yourCompany.whatever
    
    追加Build Phases->Compile Sources下面的SvUDIDTools.m文件配置信息字符串“-fno-objc-arc”
    
    修改Build Setting->Code Signing->Code Signing Entitlements属性为KeychainAccessGroups.plist
    
    */
    
    /*用法2:自己实际操作
    Project -> Capabilities -> Keychain Sharing -> on, 会弹出提示框请求Fetching list of teams from the Developer Portal....,选择开发账号,确认. 生成entitlements文件, 网络不好时,可手动添加 KeychainAccessGroups.plist 用法类似 用法1.
        自动生成的entitlements 已经将Keychain Access Groups 补充好, 不需再更改,Keychain Access Groups分组中会出现 $(AppIdentifierPrefix)com.moule.Utils-iOS ( $(AppIdentifierPrefix)开头的字段 ), 
    
        Build Phases->Compile Sources下面的SvUDIDTools.m文件配置信息字符串“-fno-objc-arc”
        
    
    //获取开发账号的appID
    + (NSString *)bundleSeedID {
        NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:kSecClassGenericPassword, kSecClass,@"bundleSeedID", kSecAttrAccount,@"", kSecAttrService,(id)kCFBooleanTrue, kSecReturnAttributes,nil];
        CFDictionaryRef result = nil;
        OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&result);
        if (status == errSecItemNotFound)
            status = SecItemAdd((CFDictionaryRef)query, (CFTypeRef *)&result);
                if (status != errSecSuccess)
                    return nil;
            NSString *accessGroup = [(__bridge NSDictionary *)result objectForKey:kSecAttrAccessGroup];
            NSArray *components = [accessGroup componentsSeparatedByString:@"."];
            NSString *bundleSeedID = [[components objectEnumerator] nextObject];
            CFRelease(result);
        return bundleSeedID;
    }
    
    kKeyChainUDIDAccessGroup 是应用的bundle ID
    
    */
    
    .h文件
    
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    @interface SvUDIDTools : NSObject
    
    
    /*
     * @brief obtain Unique Device Identity
     */
    + (NSString*)UDID;
    
    @end
    
    .m文件
    
    //
    //  SvUDIDTools.m
    //  SvUDID
    //
    //  Created by  maple on 8/18/13.
    //  Copyright (c) 2013 maple. All rights reserved.
    //
    
    #import "SvUDIDTools.h"
    #import <Security/Security.h>
    #include <sys/socket.h>
    #include <sys/sysctl.h>
    #include <net/if.h>
    #include <net/if_dl.h>
    
    // replace the identity with your company's domain
    static const char kKeychainUDIDItemIdentifier[]  = "UUID";
    static NSString * kKeyChainUDIDAccessGroup = @"com.moule.Utils-iOS";
    
    @implementation SvUDIDTools
    
    + (NSString *)bundleSeedID {
        NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
                               kSecClassGenericPassword, kSecClass,
                               @"bundleSeedID", kSecAttrAccount,
                               @"", kSecAttrService,
                               (id)kCFBooleanTrue, kSecReturnAttributes,
                               nil];
        CFDictionaryRef result = nil;
        OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&result);
        if (status == errSecItemNotFound)
            status = SecItemAdd((CFDictionaryRef)query, (CFTypeRef *)&result);
        if (status != errSecSuccess)
            return nil;
        NSString *accessGroup = [(__bridge NSDictionary *)result objectForKey:kSecAttrAccessGroup];
        NSArray *components = [accessGroup componentsSeparatedByString:@"."];
        NSString *bundleSeedID = [[components objectEnumerator] nextObject];
        CFRelease(result);
        return bundleSeedID;
    }
    
    + (NSString*)UDID
    {
        NSString *udid = [SvUDIDTools getUDIDFromKeyChain];
        if (!udid) {
            
            NSString *sysVersion = [UIDevice currentDevice].systemVersion;
            CGFloat version = [sysVersion floatValue];
        
            if (version >= 7.0) {
                udid = [SvUDIDTools _UDID_iOS7];
            }
            else if (version >= 2.0) {
                udid = [SvUDIDTools _UDID_iOS6];
            }
        
            [SvUDIDTools settUDIDToKeyChain:udid];
        }
        
        return udid;
    }
    
    /*
     * iOS 6.0
     * use wifi's mac address
     */
    + (NSString*)_UDID_iOS6
    {
        return [SvUDIDTools getMacAddress];
    }
    
    /*
     * iOS 7.0
     * Starting from iOS 7, the system always returns the value 02:00:00:00:00:00 
     * when you ask for the MAC address on any device.
     * use identifierForVendor + keyChain
     * make sure UDID consistency atfer app delete and reinstall
     */
    + (NSString*)_UDID_iOS7
    {
        return [[UIDevice currentDevice].identifierForVendor UUIDString];
    }
    
    
    #pragma mark -
    #pragma mark Helper Method for Get Mac Address
    
    // from http://stackoverflow.com/questions/677530/how-can-i-programmatically-get-the-mac-address-of-an-iphone
    + (NSString *)getMacAddress
    {
        int                 mgmtInfoBase[6];
        char                *msgBuffer = NULL;
        size_t              length;
        unsigned char       macAddress[6];
        struct if_msghdr    *interfaceMsgStruct;
        struct sockaddr_dl  *socketStruct;
        NSString            *errorFlag = nil;
        
        // Setup the management Information Base (mib)
        mgmtInfoBase[0] = CTL_NET;        // Request network subsystem
        mgmtInfoBase[1] = AF_ROUTE;       // Routing table info
        mgmtInfoBase[2] = 0;
        mgmtInfoBase[3] = AF_LINK;        // Request link layer information
        mgmtInfoBase[4] = NET_RT_IFLIST;  // Request all configured interfaces
        
        // With all configured interfaces requested, get handle index
        if ((mgmtInfoBase[5] = if_nametoindex("en0")) == 0)
            errorFlag = @"if_nametoindex failure";
        else
        {
            // Get the size of the data available (store in len)
            if (sysctl(mgmtInfoBase, 6, NULL, &length, NULL, 0) < 0)
                errorFlag = @"sysctl mgmtInfoBase failure";
            else
            {
                // Alloc memory based on above call
                if ((msgBuffer = malloc(length)) == NULL)
                    errorFlag = @"buffer allocation failure";
                else
                {
                    // Get system information, store in buffer
                    if (sysctl(mgmtInfoBase, 6, msgBuffer, &length, NULL, 0) < 0)
                        errorFlag = @"sysctl msgBuffer failure";
                }
            }
        }
        
        // Befor going any further...
        if (errorFlag != NULL)
        {
            NSLog(@"Error: %@", errorFlag);
            if (msgBuffer) {
                free(msgBuffer);
            }
            
            return errorFlag;
        }
        
        // Map msgbuffer to interface message structure
        interfaceMsgStruct = (struct if_msghdr *) msgBuffer;
        
        // Map to link-level socket structure
        socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);
        
        // Copy link layer address data in socket structure to an array
        memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen, 6);
        
        // Read from char array into a string object, into traditional Mac address format
        NSString *macAddressString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
                                      macAddress[0], macAddress[1], macAddress[2],
                                      macAddress[3], macAddress[4], macAddress[5]];
        NSLog(@"Mac Address: %@", macAddressString);
        
        // Release the buffer memory
        free(msgBuffer);
        
        return macAddressString;
    }
    
    #pragma mark -
    #pragma mark Helper Method for make identityForVendor consistency
    
    + (NSString*)getUDIDFromKeyChain
    {
        NSMutableDictionary *dictForQuery = [[NSMutableDictionary alloc] init];
        [dictForQuery setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
        
        // set Attr Description for query
        [dictForQuery setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier]
                        forKey:kSecAttrDescription];
        
        // set Attr Identity for query
        NSData *keychainItemID = [NSData dataWithBytes:kKeychainUDIDItemIdentifier
                                                length:strlen(kKeychainUDIDItemIdentifier)];
        [dictForQuery setObject:keychainItemID forKey:(id)kSecAttrGeneric];
        
        // The keychain access group attribute determines if this item can be shared
        // amongst multiple apps whose code signing entitlements contain the same keychain access group.
        NSString *accessGroup =[NSString stringWithFormat:@"%@.%@",[SvUDIDTools bundleSeedID],kKeyChainUDIDAccessGroup];
        if (accessGroup != nil)
        {
    #if TARGET_IPHONE_SIMULATOR
            // Ignore the access group if running on the iPhone simulator.
            //
            // Apps that are built for the simulator aren't signed, so there's no keychain access group
            // for the simulator to check. This means that all apps can see all keychain items when run
            // on the simulator.
            //
            // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
            // simulator will return -25243 (errSecNoAccessForItem).
    #else
            [dictForQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
    #endif
        }
        
        [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecMatchCaseInsensitive];
        [dictForQuery setValue:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
        [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
        
        OSStatus queryErr   = noErr;
        NSData   *udidValue = nil;
        NSString *udid      = nil;
        queryErr = SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&udidValue);
        
        NSMutableDictionary *dict = nil;
        [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
        queryErr = SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&dict);
        
        if (queryErr == errSecItemNotFound) {
            NSLog(@"KeyChain Item: %@ not found!!!", [NSString stringWithUTF8String:kKeychainUDIDItemIdentifier]);
        }
        else if (queryErr != errSecSuccess) {
            NSLog(@"KeyChain Item query Error!!! Error code:%d", (int)queryErr);
        }
        if (queryErr == errSecSuccess) {
            NSLog(@"KeyChain Item: %@", udidValue);
            
            if (udidValue) {
                udid = [NSString stringWithUTF8String:udidValue.bytes];
                [udidValue release];
            }
            [dict release];
        }
        
        [dictForQuery release];
        return udid;
    }
    
    + (BOOL)settUDIDToKeyChain:(NSString*)udid
    {
        NSMutableDictionary *dictForAdd = [[NSMutableDictionary alloc] init];
        
        [dictForAdd setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
        [dictForAdd setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier] forKey:kSecAttrDescription];
        
        [dictForAdd setValue:@"UUID" forKey:(id)kSecAttrGeneric];
        
        // Default attributes for keychain item.
        [dictForAdd setObject:@"" forKey:(id)kSecAttrAccount];
        [dictForAdd setObject:@"" forKey:(id)kSecAttrLabel];
        
        
        // The keychain access group attribute determines if this item can be shared
        // amongst multiple apps whose code signing entitlements contain the same keychain access group.
        NSString *accessGroup = [NSString stringWithFormat:@"%@.%@",[SvUDIDTools bundleSeedID],kKeyChainUDIDAccessGroup];
        if (accessGroup != nil)
        {
    #if TARGET_IPHONE_SIMULATOR
            // Ignore the access group if running on the iPhone simulator.
            //
            // Apps that are built for the simulator aren't signed, so there's no keychain access group
            // for the simulator to check. This means that all apps can see all keychain items when run
            // on the simulator.
            //
            // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
            // simulator will return -25243 (errSecNoAccessForItem).
    #else
            [dictForAdd setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
    #endif
        }
    
        const char *udidStr = [udid UTF8String];
        NSData *keyChainItemValue = [NSData dataWithBytes:udidStr length:strlen(udidStr)];
        [dictForAdd setValue:keyChainItemValue forKey:(id)kSecValueData];
        
        OSStatus writeErr = noErr;
        if ([SvUDIDTools getUDIDFromKeyChain]) {        // there is item in keychain
            [SvUDIDTools updateUDIDInKeyChain:udid];
            [dictForAdd release];
            return YES;
        }
        else {          // add item to keychain
            writeErr = SecItemAdd((CFDictionaryRef)dictForAdd, NULL);
            if (writeErr != errSecSuccess) {
                NSLog(@"Add KeyChain Item Error!!! Error Code:%ld", writeErr);
                
                [dictForAdd release];
                return NO;
            }
            else {
                NSLog(@"Add KeyChain Item Success!!!");
                [dictForAdd release];
                return YES;
            }
        }
        
        [dictForAdd release];
        return NO;
    }
    
    + (BOOL)removeUDIDFromKeyChain
    {
        NSMutableDictionary *dictToDelete = [[NSMutableDictionary alloc] init];
        
        [dictToDelete setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
        
        NSData *keyChainItemID = [NSData dataWithBytes:kKeychainUDIDItemIdentifier length:strlen(kKeychainUDIDItemIdentifier)];
        [dictToDelete setValue:keyChainItemID forKey:(id)kSecAttrGeneric];
        
        OSStatus deleteErr = noErr;
        deleteErr = SecItemDelete((CFDictionaryRef)dictToDelete);
        if (deleteErr != errSecSuccess) {
            NSLog(@"delete UUID from KeyChain Error!!! Error code:%ld", deleteErr);
            [dictToDelete release];
            return NO;
        }
        else {
            NSLog(@"delete success!!!");
        }
        
        [dictToDelete release];
        return YES;
    }
    
    + (BOOL)updateUDIDInKeyChain:(NSString*)newUDID
    {
        
        NSMutableDictionary *dictForQuery = [[NSMutableDictionary alloc] init];
        
        [dictForQuery setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
       
        NSData *keychainItemID = [NSData dataWithBytes:kKeychainUDIDItemIdentifier
                                                length:strlen(kKeychainUDIDItemIdentifier)];
        [dictForQuery setValue:keychainItemID forKey:(id)kSecAttrGeneric];
        [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecMatchCaseInsensitive];
        [dictForQuery setValue:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
        [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
        
        NSDictionary *queryResult = nil;
        SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&queryResult);
        if (queryResult) {
            
            NSMutableDictionary *dictForUpdate = [[NSMutableDictionary alloc] init];
            [dictForUpdate setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier] forKey:kSecAttrDescription];
            [dictForUpdate setValue:keychainItemID forKey:(id)kSecAttrGeneric];
            
            const char *udidStr = [newUDID UTF8String];
            NSData *keyChainItemValue = [NSData dataWithBytes:udidStr length:strlen(udidStr)];
            [dictForUpdate setValue:keyChainItemValue forKey:(id)kSecValueData];
            
            OSStatus updateErr = noErr;
            
            // First we need the attributes from the Keychain.
            NSMutableDictionary *updateItem = [NSMutableDictionary dictionaryWithDictionary:queryResult];
            [queryResult release];
            
            // Second we need to add the appropriate search key/values.
            // set kSecClass is Very important
            [updateItem setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
            
            updateErr = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)dictForUpdate);
            if (updateErr != errSecSuccess) {
                NSLog(@"Update KeyChain Item Error!!! Error Code:%ld", updateErr);
                
                [dictForQuery release];
                [dictForUpdate release];
                return NO;
            }
            else {
                NSLog(@"Update KeyChain Item Success!!!");
                [dictForQuery release];
                [dictForUpdate release];
                return YES;
            }
        }
        
        [dictForQuery release];
        return NO;
    }
    
    @end
    
  • 相关阅读:
    IO流之File类
    CPU单核多核区别【转载】
    [转载] MySQL的四种事务隔离级别
    事务的四大特性,以及隔离级别
    [转载] spring aop 环绕通知around和其他通知的区别
    [转载] Spring框架——AOP前置、后置、环绕、异常通知
    spring框架 AOP核心详解
    汇编语言--操作数的寻址方式(三)
    汇编语言--CPU资源和存储器(二)
    汇编语言学习--基础知识(一)
  • 原文地址:https://www.cnblogs.com/levy/p/5027205.html
Copyright © 2011-2022 走看看