SvUDIDTools.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. //
  2. // SvUDIDTools.m
  3. // SvUDID
  4. //
  5. // Created by maple on 8/18/13.
  6. // Copyright (c) 2013 maple. All rights reserved.
  7. //
  8. #import "SvUDIDTools.h"
  9. #import <Security/Security.h>
  10. #include <sys/socket.h>
  11. #include <sys/sysctl.h>
  12. #include <net/if.h>
  13. #include <net/if_dl.h>
  14. // replace the identity with your company's domain
  15. static const char kKeychainUDIDItemIdentifier[] = "UUID";
  16. static const char kKeyChainUDIDAccessGroup[] = "YH5ZPZJ2HP.com.cnblogs.smileEvday";
  17. @implementation SvUDIDTools
  18. + (NSString*)OnlyUDID
  19. {
  20. NSString *udid = [SvUDIDTools getUDIDFromKeyChain];
  21. if (!udid) {
  22. NSString *sysVersion = [UIDevice currentDevice].systemVersion;
  23. CGFloat version = [sysVersion floatValue];
  24. if (version >= 7.0) {
  25. udid = [SvUDIDTools _UDID_iOS7];
  26. }
  27. else if (version >= 2.0) {
  28. udid = [SvUDIDTools _UDID_iOS6];
  29. }
  30. [SvUDIDTools settUDIDToKeyChain:udid];
  31. }
  32. return udid;
  33. }
  34. /*
  35. * iOS 6.0
  36. * use wifi's mac address
  37. */
  38. + (NSString*)_UDID_iOS6
  39. {
  40. return [SvUDIDTools getMacAddress];
  41. }
  42. /*
  43. * iOS 7.0
  44. * Starting from iOS 7, the system always returns the value 02:00:00:00:00:00
  45. * when you ask for the MAC address on any device.
  46. * use identifierForVendor + keyChain
  47. * make sure UDID consistency atfer app delete and reinstall
  48. */
  49. + (NSString*)_UDID_iOS7
  50. {
  51. return [[UIDevice currentDevice].identifierForVendor UUIDString];
  52. }
  53. #pragma mark -
  54. #pragma mark Helper Method for Get Mac Address
  55. // from http://stackoverflow.com/questions/677530/how-can-i-programmatically-get-the-mac-address-of-an-iphone
  56. + (NSString *)getMacAddress
  57. {
  58. int mgmtInfoBase[6];
  59. char *msgBuffer = NULL;
  60. size_t length;
  61. unsigned char macAddress[6];
  62. struct if_msghdr *interfaceMsgStruct;
  63. struct sockaddr_dl *socketStruct;
  64. NSString *errorFlag = nil;
  65. // Setup the management Information Base (mib)
  66. mgmtInfoBase[0] = CTL_NET; // Request network subsystem
  67. mgmtInfoBase[1] = AF_ROUTE; // Routing table info
  68. mgmtInfoBase[2] = 0;
  69. mgmtInfoBase[3] = AF_LINK; // Request link layer information
  70. mgmtInfoBase[4] = NET_RT_IFLIST; // Request all configured interfaces
  71. // With all configured interfaces requested, get handle index
  72. if ((mgmtInfoBase[5] = if_nametoindex("en0")) == 0)
  73. errorFlag = @"if_nametoindex failure";
  74. else
  75. {
  76. // Get the size of the data available (store in len)
  77. if (sysctl(mgmtInfoBase, 6, NULL, &length, NULL, 0) < 0)
  78. errorFlag = @"sysctl mgmtInfoBase failure";
  79. else
  80. {
  81. // Alloc memory based on above call
  82. if ((msgBuffer = malloc(length)) == NULL)
  83. errorFlag = @"buffer allocation failure";
  84. else
  85. {
  86. // Get system information, store in buffer
  87. if (sysctl(mgmtInfoBase, 6, msgBuffer, &length, NULL, 0) < 0)
  88. errorFlag = @"sysctl msgBuffer failure";
  89. }
  90. }
  91. }
  92. // Befor going any further...
  93. if (errorFlag != NULL)
  94. {
  95. NSLog(@"Error: %@", errorFlag);
  96. if (msgBuffer) {
  97. free(msgBuffer);
  98. }
  99. return errorFlag;
  100. }
  101. // Map msgbuffer to interface message structure
  102. interfaceMsgStruct = (struct if_msghdr *) msgBuffer;
  103. // Map to link-level socket structure
  104. socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);
  105. // Copy link layer address data in socket structure to an array
  106. memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen, 6);
  107. // Read from char array into a string object, into traditional Mac address format
  108. NSString *macAddressString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
  109. macAddress[0], macAddress[1], macAddress[2],
  110. macAddress[3], macAddress[4], macAddress[5]];
  111. NSLog(@"Mac Address: %@", macAddressString);
  112. // Release the buffer memory
  113. free(msgBuffer);
  114. return macAddressString;
  115. }
  116. #pragma mark -
  117. #pragma mark Helper Method for make identityForVendor consistency
  118. + (NSString*)getUDIDFromKeyChain
  119. {
  120. NSMutableDictionary *dictForQuery = [[NSMutableDictionary alloc] init];
  121. [dictForQuery setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
  122. // set Attr Description for query
  123. [dictForQuery setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier]
  124. forKey:kSecAttrDescription];
  125. // set Attr Identity for query
  126. NSData *keychainItemID = [NSData dataWithBytes:kKeychainUDIDItemIdentifier
  127. length:strlen(kKeychainUDIDItemIdentifier)];
  128. [dictForQuery setObject:keychainItemID forKey:(id)kSecAttrGeneric];
  129. // The keychain access group attribute determines if this item can be shared
  130. // amongst multiple apps whose code signing entitlements contain the same keychain access group.
  131. NSString *accessGroup = [NSString stringWithUTF8String:kKeyChainUDIDAccessGroup];
  132. if (accessGroup != nil)
  133. {
  134. #if TARGET_IPHONE_SIMULATOR
  135. // Ignore the access group if running on the iPhone simulator.
  136. //
  137. // Apps that are built for the simulator aren't signed, so there's no keychain access group
  138. // for the simulator to check. This means that all apps can see all keychain items when run
  139. // on the simulator.
  140. //
  141. // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
  142. // simulator will return -25243 (errSecNoAccessForItem).
  143. #else
  144. [dictForQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
  145. #endif
  146. }
  147. [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecMatchCaseInsensitive];
  148. [dictForQuery setValue:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
  149. [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
  150. OSStatus queryErr = noErr;
  151. NSData *udidValue = nil;
  152. NSString *udid = nil;
  153. queryErr = SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&udidValue);
  154. NSMutableDictionary *dict = nil;
  155. [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
  156. queryErr = SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&dict);
  157. if (queryErr == errSecItemNotFound) {
  158. NSLog(@"KeyChain Item: %@ not found!!!", [NSString stringWithUTF8String:kKeychainUDIDItemIdentifier]);
  159. }
  160. else if (queryErr != errSecSuccess) {
  161. NSLog(@"KeyChain Item query Error!!! Error code:%d", (int)queryErr);
  162. }
  163. if (queryErr == errSecSuccess) {
  164. NSLog(@"KeyChain Item: %@", udidValue);
  165. if (udidValue) {
  166. udid = [NSString stringWithUTF8String:udidValue.bytes];
  167. }
  168. }
  169. [dictForQuery release];
  170. return udid;
  171. }
  172. + (BOOL)settUDIDToKeyChain:(NSString*)udid
  173. {
  174. NSMutableDictionary *dictForAdd = [[NSMutableDictionary alloc] init];
  175. [dictForAdd setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
  176. [dictForAdd setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier] forKey:kSecAttrDescription];
  177. [dictForAdd setValue:@"UUID" forKey:(id)kSecAttrGeneric];
  178. // Default attributes for keychain item.
  179. [dictForAdd setObject:@"" forKey:(id)kSecAttrAccount];
  180. [dictForAdd setObject:@"" forKey:(id)kSecAttrLabel];
  181. // The keychain access group attribute determines if this item can be shared
  182. // amongst multiple apps whose code signing entitlements contain the same keychain access group.
  183. NSString *accessGroup = [NSString stringWithUTF8String:kKeyChainUDIDAccessGroup];
  184. if (accessGroup != nil)
  185. {
  186. #if TARGET_IPHONE_SIMULATOR
  187. // Ignore the access group if running on the iPhone simulator.
  188. //
  189. // Apps that are built for the simulator aren't signed, so there's no keychain access group
  190. // for the simulator to check. This means that all apps can see all keychain items when run
  191. // on the simulator.
  192. //
  193. // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
  194. // simulator will return -25243 (errSecNoAccessForItem).
  195. #else
  196. [dictForAdd setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
  197. #endif
  198. }
  199. const char *udidStr = [udid UTF8String];
  200. NSData *keyChainItemValue = [NSData dataWithBytes:udidStr length:strlen(udidStr)];
  201. [dictForAdd setValue:keyChainItemValue forKey:(id)kSecValueData];
  202. OSStatus writeErr = noErr;
  203. if ([SvUDIDTools getUDIDFromKeyChain]) { // there is item in keychain
  204. [SvUDIDTools updateUDIDInKeyChain:udid];
  205. [dictForAdd release];
  206. return YES;
  207. }
  208. else { // add item to keychain
  209. writeErr = SecItemAdd((CFDictionaryRef)dictForAdd, NULL);
  210. if (writeErr != errSecSuccess) {
  211. NSLog(@"Add KeyChain Item Error!!! Error Code:%d", (int)writeErr);
  212. [dictForAdd release];
  213. return NO;
  214. }
  215. else {
  216. NSLog(@"Add KeyChain Item Success!!!");
  217. [dictForAdd release];
  218. return YES;
  219. }
  220. }
  221. [dictForAdd release];
  222. return NO;
  223. }
  224. + (BOOL)removeUDIDFromKeyChain
  225. {
  226. NSMutableDictionary *dictToDelete = [[NSMutableDictionary alloc] init];
  227. [dictToDelete setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
  228. NSData *keyChainItemID = [NSData dataWithBytes:kKeychainUDIDItemIdentifier length:strlen(kKeychainUDIDItemIdentifier)];
  229. [dictToDelete setValue:keyChainItemID forKey:(id)kSecAttrGeneric];
  230. OSStatus deleteErr = noErr;
  231. deleteErr = SecItemDelete((CFDictionaryRef)dictToDelete);
  232. if (deleteErr != errSecSuccess) {
  233. NSLog(@"delete UUID from KeyChain Error!!! Error code:%d", (int)deleteErr);
  234. [dictToDelete release];
  235. return NO;
  236. }
  237. else {
  238. NSLog(@"delete success!!!");
  239. }
  240. [dictToDelete release];
  241. return YES;
  242. }
  243. + (BOOL)updateUDIDInKeyChain:(NSString*)newUDID
  244. {
  245. NSMutableDictionary *dictForQuery = [[NSMutableDictionary alloc] init];
  246. [dictForQuery setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
  247. NSData *keychainItemID = [NSData dataWithBytes:kKeychainUDIDItemIdentifier
  248. length:strlen(kKeychainUDIDItemIdentifier)];
  249. [dictForQuery setValue:keychainItemID forKey:(id)kSecAttrGeneric];
  250. [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecMatchCaseInsensitive];
  251. [dictForQuery setValue:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
  252. [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
  253. NSDictionary *queryResult = nil;
  254. SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&queryResult);
  255. if (queryResult) {
  256. NSMutableDictionary *dictForUpdate = [[NSMutableDictionary alloc] init];
  257. [dictForUpdate setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier] forKey:kSecAttrDescription];
  258. [dictForUpdate setValue:keychainItemID forKey:(id)kSecAttrGeneric];
  259. const char *udidStr = [newUDID UTF8String];
  260. NSData *keyChainItemValue = [NSData dataWithBytes:udidStr length:strlen(udidStr)];
  261. [dictForUpdate setValue:keyChainItemValue forKey:(id)kSecValueData];
  262. OSStatus updateErr = noErr;
  263. // First we need the attributes from the Keychain.
  264. NSMutableDictionary *updateItem = [NSMutableDictionary dictionaryWithDictionary:queryResult];
  265. // Second we need to add the appropriate search key/values.
  266. // set kSecClass is Very important
  267. [updateItem setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
  268. updateErr = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)dictForUpdate);
  269. if (updateErr != errSecSuccess) {
  270. NSLog(@"Update KeyChain Item Error!!! Error Code:%d", (int)updateErr);
  271. [dictForQuery release];
  272. [dictForUpdate release];
  273. return NO;
  274. }
  275. else {
  276. NSLog(@"Update KeyChain Item Success!!!");
  277. [dictForQuery release];
  278. [dictForUpdate release];
  279. return YES;
  280. }
  281. }
  282. [dictForQuery release];
  283. return NO;
  284. }
  285. @end