天天看點

擷取唯一辨別UUID UDID

安卓裡面的IMEI IMSI是可以随便擷取的,但是蘋果現在已經此資訊封死了,開發者是無法擷取到的,因為蘋果注重的是使用者的隐私

//此段摘自http://www.cnblogs.com/smileEvday/p/UDID.html

iOS不同版本擷取UDID的方法比較

  1)iOS 5.0

  iOS 2.0版本以後UIDevice提供一個擷取裝置唯一辨別符的方法uniqueIdentifier,通過該方法我們可以擷取裝置的序列号,這個也是目前為止唯一可以确認唯一的标示符。好景不長,因為該唯一辨別符與手機一一對應,蘋果覺得可能會洩露使用者隐私,是以在 iOS 5.0之後該方法就被廢棄掉了。

  而且蘋果做的更狠,今年5月份以後送出App Store的産品都不允許再用uniqueIdentifier接口,甚至有些朋友因為代碼中有UDID還被打回來,看來這條路是被封死了。

  2)iOS 6.0

  iOS 6.0系統新增了兩個用于替換uniqueIdentifier的接口,分别是:identifierForVendor,advertisingIdentifier。

  identifierForVendor接口的官方文檔介紹如下:

The value of this property is the same for apps that come from the same vendor running on the same device. A different value is returned for apps on the same device that come from different vendors, and for apps on different devices regardless of vendor.

The value of this property may be nil if the app is running in the background, before the user has unlocked the device the first time after the device has been restarted. If the value is nil, wait and get the value again later.

The value in this property remains the same while the app (or another app from the same vendor) is installed on the iOS device. The value changes when the user deletes all of that vendor’s apps from the device and subsequently reinstalls one or more of them. Therefore, if your app stores the value of this property anywhere, you should gracefully handle situations where the identifier changes.

大概意思就是“同一開發商的APP在指定機器上都會獲得同一個ID。當我們删除了某一個裝置上某個開發商的所有APP之後,下次擷取将會擷取到不同的ID。” 也就是說我們通過該接口不能擷取用來唯一辨別裝置的ID,問題總是難不倒聰明的程式員,于是大家想到了使用WiFi的mac位址來取代已經廢棄了的uniqueIdentifier方法。

  3)iOS 7.0  

  iOS 7中蘋果再一次無情的封殺mac位址,使用之前的方法擷取到的mac位址全部都變成了02:00:00:00:00:00。有問題總的解決啊,于是四處查資料,終于有了思路是否可以使用KeyChain來儲存擷取到的唯一标示符呢,這樣以後即使APP删了再裝回來,也可以從KeyChain中讀取回來。有了方向以後就開始做,看關于KeyChain的官方文檔,看官方使用KeyChain的Demo,大概花了一下午時間,問題終于解決了。 

使用KeyChain實作APP删除後依然可以擷取到相同的UDID資訊的解決方法

就算我們程式删除掉,系統經過更新以後再安裝回來,依舊可以擷取到與之前一緻的UDID。但是當我們把整個系統還原以後是否還能擷取到之前記錄的UDID,這一點應該不行

下面附上類檔案,大家直接建立類,把代碼全部粘貼過去就可以了

//  SvUDIDTools.h

#import <Foundation/Foundation.h>

@interface SvUDIDTools : NSObject

+ (NSString*)UDID;

@end

//  SvUDIDTools.m

#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 const char kKeyChainUDIDAccessGroup[] = "YOURAPPID.com.cnblogs.smileEvday";

@implementation SvUDIDTools

+ (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;

}

+ (NSString*)_UDID_iOS6

{

    return [SvUDIDTools getMacAddress];

}

+ (NSString*)_UDID_iOS7

{

    //IOS7就差keychain了,因為identifierforvendor隻要不删除程式都 是唯一 的

    //keychain是隻要不重裝都 是唯一,弄起來麻煩耗時

    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 stringWithUTF8String: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:%ld", queryErr);

    }

    if (queryErr == errSecSuccess) {

        NSLog(@"KeyChain Item: %@", udidValue);

        if (udidValue) {

            udid = [NSString stringWithUTF8String:udidValue.bytes];

        }

    }

    [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 stringWithUTF8String: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];

        // 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