大家好,欢迎来到IT知识分享网。
WWDC 2013已经闭幕,IOS7 Beta随即发布,界面之难看无以言表…,简直就是山寨Android。
更让IOS程序猿悲催的是,设备唯一标识的MAC Address在IOS7中也失效了。
IOS系统中,获取设备唯一标识的方法有很多:
一.UDID(Unique Device Identifier)
UDID的全称是Unique Device Identifier,顾名思义,它就是苹果IOS设备的唯一识别码,它由40个字符的字母和数字组成。
二.UUID(Universally Unique Identifier)
UUID是Universally Unique Identifier的缩写,中文意思是通用唯一识别码.
三.MAC Address
四.OPEN UDID
五.广告标示符(IDFA-identifierForIdentifier)
六.Vindor标示符 (IDFV-identifierForVendor)
Vendor是CFBundleIdentifier(反转DNS格式)的前两部分。来自同一个运营商的应用运行在同一个设备上,此属性的值是相同的;不同的运营商应用运行在同一个设备上值不同。
经测试,只要设备上有一个tencent的app,重新安装后的identifierForVendor值不变,如果tencent的app全部删除,重新安装后的identifierForVendor值改变。
但是很不幸,上面所有这些表示设备唯一号的标识,在IOS7中要么被禁止使用,要么重新安装程序后两次获取的标识符不一样。
由于IOS系统存储的数据都是在sandBox里面,一旦删除App,sandBox也不复存在。好在有一个例外,那就是keychain(钥匙串)。
通常情况下,IOS系统用NSUserDefaults存储数据信息,但是对于一些私密信息,比如密码、证书等等,就需要使用更为安全的keychain了。
keychain里保存的信息不会因App被删除而丢失。所以,可以利用这个keychain这个特点来保存设备唯一标识。
那么,如何在应用里使用使用keyChain呢,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。
直接使用SecItem.h里方法操作keychain,需要写的代码较为复杂,我们可以使用已经封装好了的工具类KeychainItemWrapper来对keychain进行操作。
KeychainItemWrapper是apple官方例子“GenericKeychain”里一个访问keychain常用操作的封装类,在官网上下载了GenericKeychain项目后,
只需要把“KeychainItemWrapper.h”和“KeychainItemWrapper.m”拷贝到我们项目,并导入Security.framework 。KeychainItemWrapper的用法:
*wrapper
=
[
[KeychainItemWrapper alloc
] initWithIdentifier
:@
“Account Number”
:@
“YOUR_APP_ID_HERE.com.yourcompany.AppIdentifier”
]
;
:@
“<帐号>” forKey
:
(id
)kSecAttrAccount
]
;
:@
“<帐号密码>” forKey
:
(id
)kSecValueData
]
;
*password
=
[wrapper objectForKey
:
(id
)kSecValueData
]
;
]
;
其中方法“- (void)setObject:(id)inObject forKey:(id)key;”里参数“forKey”的值应该是Security.framework 里头文件“SecItem.h”里定义好的key,用其他字符串做key程序会出错!
// // SFHFKeychainUtils.h // // Created by Buzz Andersen on 10/20/08. // Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone. // Copyright 2008 Sci-Fi Hi-Fi. All rights reserved. // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // #import <UIKit/UIKit.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 // // Created by Buzz Andersen on 10/20/08. // Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone. // Copyright 2008 Sci-Fi Hi-Fi. All rights reserved. // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // #import "SFHFKeychainUtils.h" #import <Security/Security.h> 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]; NSArray *objects = [[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, nil]; NSMutableDictionary *query = [[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys]; // 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); 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; } + (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]; NSArray *objects = [[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, serviceName, serviceName, username, nil]; NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys]; status = SecItemUpdate((CFDictionaryRef) query, (CFDictionaryRef) [NSDictionary dictionaryWithObject: [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]; NSArray *objects = [[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, serviceName, serviceName, username, [password dataUsingEncoding: NSUTF8StringEncoding], nil]; NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys]; 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]; NSArray *objects = [[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil]; NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys]; 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
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/143787.html