UUID、UDID和KeyChain
UDID:Unique Device Identifier。它就是苹果IOS设备的唯一识别码,它由40个字符的字母和数字组成。
UUID:Universally Unique Identifier。中文意思是通用唯一识别码。
禁止UDID是苹果为了保护用户的隐私,UUID则是给出的替代方案。
/*设备唯一ID(删除应用后重装ID不变)*/
- (NSString *)UDID
{
NSString *UDID = [[NSUserDefaults standardUserDefaults] valueForKey:@"UDID"];
if (!UDID.length) {
if (self.systemVersion.floatValue >= 7.0) {//在keychain中持久化
UDID = [self getUDIDFromKeychain];
if (!UDID.length) {
UDID = self.identifierForVendor.UUIDString;
[self setUDIDToKeychain:UDID];
}
} else {//MAC地址的MD5串
UDID = [self.MACAddress MD5EncodedString];
}
[[NSUserDefaults standardUserDefaults] setValue:UDID forKey:@"UDID"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
return [UDID stringByReplacingOccurrencesOfString:@"-" withString:@""];
}
/*设备唯一ID(删除应用重装后ID可能变更)*/
- (NSString *)UUID
{
NSString *UUID = nil;
if (self.systemVersion.floatValue >= 6.0) {
UUID = self.identifierForVendor.UUIDString;
} else {
UUID = [[NSUserDefaults standardUserDefaults] valueForKey:@"UUID"];
if (!UUID.length) {
CFUUIDRef UUIDRef = CFUUIDCreate(NULL);
UUID = (__bridge NSString *)CFUUIDCreateString(NULL, UUIDRef);
CFRelease(UUIDRef);
[[NSUserDefaults standardUserDefaults] setValue:UUID forKey:@"UUID"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
return [UUID stringByReplacingOccurrencesOfString:@"-" withString:@""];
}
KeyChain:
通常情况下,我们用NSUserDefaults存储数据信息,但是对于一些私密信息,比如密码、证书等等,就需要使用更为安全的keychain了。keychain里保存的信息不会因App被删除而丢失,在用户重新安装App后依然有效,数据还在。
使用苹果官方发布的KeychainItemWrapper或者SFHFKeychainUtils很方便,需要导入Security.framework。每个ios程序都有一个独立的keychain存储。
在应用里使用使用keyChain,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。直接使用SecItem.h里方法操作keychain,需要写的代码较为复杂,为减轻程序的开发,可以使用一些已经封装好了的工具类:KeychainItemWrapper和 SFHFKeychainUtils。
Getting Started
A couple of housekeeping items to get started:
- Add the “Security.framework” framework to your iPhone application
- Include the header file <Security/Security.h>
The Basic Search Dictionary
All of the calls to the keychain services make use of a dictionary to define the attributes of the keychain item you want to find, create, update or delete. So the first thing we will do is define a function to allocate and construct this dictionary for us:static NSString *serviceName = @"com.mycompany.myAppServiceName";
- (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
[searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
[searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrGeneric];
[searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrAccount];
[searchDictionary setObject:serviceName forKey:(id)kSecAttrService];
return searchDictionary;
}
Searching the keychain
To find out if our password already exists in the keychain (and what the value of the password is) we use the SecItemCopyMatching function. But first we add a couple of extra items to our basic search dictionary:
- (NSData *)searchKeychainCopyMatching:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
// Add search attributes
[searchDictionary setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
// Add search return types
[searchDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
NSData *result = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)searchDictionary,
(CFTypeRef *)&result);
[searchDictionary release];
return result;
}
Creating an item in the keychain
Adding an item is almost the same as the previous examples except that we need to set the value of the password we want to store
- (BOOL)createKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier {
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:passwordData forKey:(id)kSecValueData];
OSStatus status = SecItemAdd((CFDictionaryRef)dictionary, NULL);
[dictionary release];
if (status == errSecSuccess) {
return YES;
}
return NO;
}
Updating a keychain item
Updating a keychain is similar to adding an item except that a separate dictionary is used to contain the attributes to be updated. Since in our case we are only updating a single attribute (the password) this is easy:
- (BOOL)updateKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[updateDictionary setObject:passwordData forKey:(id)kSecValueData];
OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary,
(CFDictionaryRef)updateDictionary);
[searchDictionary release];
[updateDictionary release];
if (status == errSecSuccess) {
return YES;
}
return NO;
}
Deleting an item from the keychain
The final (and easiest) operation is to delete an item from the keychain using the SecItemDelete function and our usual search dictionary:
- (void)deleteKeychainValue:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
SecItemDelete((CFDictionaryRef)searchDictionary);
[searchDictionary release];
}