iOS keyChain 钥匙串
2019-08-21 本文已影响0人
邓立_全栈UncleLi
今天刚好需要把东西存到钥匙串(keyChain)中,本项目是oc的,那就用oc实现这个鬼东西...
首先把四个文件复制进去
文件一:
//
// SAMKeychain.h
// SAMKeychain
//
//
#if __has_feature(modules)
@import Foundation;
#else
#import <Foundation/Foundation.h>
#endif
NS_ASSUME_NONNULL_BEGIN
/**
Error code specific to SAMKeychain that can be returned in NSError objects.
For codes returned by the operating system, refer to SecBase.h for your
platform.
*/
typedef NS_ENUM(OSStatus, SAMKeychainErrorCode) {
/** Some of the arguments were invalid. */
SAMKeychainErrorBadArguments = -1001,
};
/** SAMKeychain error domain */
extern NSString *const kSAMKeychainErrorDomain;
/** Account name. */
extern NSString *const kSAMKeychainAccountKey;
/**
Time the item was created.
The value will be a string.
*/
extern NSString *const kSAMKeychainCreatedAtKey;
/** Item class. */
extern NSString *const kSAMKeychainClassKey;
/** Item description. */
extern NSString *const kSAMKeychainDescriptionKey;
/** Item label. */
extern NSString *const kSAMKeychainLabelKey;
/** Time the item was last modified.
The value will be a string.
*/
extern NSString *const kSAMKeychainLastModifiedKey;
/** Where the item was created. */
extern NSString *const kSAMKeychainWhereKey;
/**
Simple wrapper for accessing accounts, getting passwords, setting passwords, and deleting passwords using the system
Keychain on Mac OS X and iOS.
This was originally inspired by EMKeychain and SDKeychain (both of which are now gone). Thanks to the authors.
SAMKeychain has since switched to a simpler implementation that was abstracted from [SSToolkit](http://sstoolk.it).
*/
@interface SAMKeychain : NSObject
#pragma mark - Classic methods
/**
Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a
password for the given parameters.
@param serviceName The service for which to return the corresponding password.
@param account The account for which to return the corresponding password.
@return Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't
have a password for the given parameters.
*/
+ (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
+ (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));
/**
Returns a nsdata containing the password for a given account and service, or `nil` if the Keychain doesn't have a
password for the given parameters.
@param serviceName The service for which to return the corresponding password.
@param account The account for which to return the corresponding password.
@return Returns a nsdata containing the password for a given account and service, or `nil` if the Keychain doesn't
have a password for the given parameters.
*/
+ (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;
+ (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));
/**
Deletes a password from the Keychain.
@param serviceName The service for which to delete the corresponding password.
@param account The account for which to delete the corresponding password.
@return Returns `YES` on success, or `NO` on failure.
*/
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));
/**
Sets a password in the Keychain.
@param password The password to store in the Keychain.
@param serviceName The service for which to set the corresponding password.
@param account The account for which to set the corresponding password.
@return Returns `YES` on success, or `NO` on failure.
*/
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));
/**
Sets a password in the Keychain.
@param password The password to store in the Keychain.
@param serviceName The service for which to set the corresponding password.
@param account The account for which to set the corresponding password.
@return Returns `YES` on success, or `NO` on failure.
*/
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));
/**
Returns an array containing the Keychain's accounts, or `nil` if the Keychain has no accounts.
See the `NSString` constants declared in SAMKeychain.h for a list of keys that can be used when accessing the
dictionaries returned by this method.
@return An array of dictionaries containing the Keychain's accounts, or `nil` if the Keychain doesn't have any
accounts. The order of the objects in the array isn't defined.
*/
+ (nullable NSArray<NSDictionary<NSString *, id> *> *)allAccounts;
+ (nullable NSArray<NSDictionary<NSString *, id> *> *)allAccounts:(NSError *__autoreleasing *)error __attribute__((swift_error(none)));
/**
Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any
accounts for the given service.
See the `NSString` constants declared in SAMKeychain.h for a list of keys that can be used when accessing the
dictionaries returned by this method.
@param serviceName The service for which to return the corresponding accounts.
@return An array of dictionaries containing the Keychain's accounts for a given `serviceName`, or `nil` if the Keychain
doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined.
*/
+ (nullable NSArray<NSDictionary<NSString *, id> *> *)accountsForService:(nullable NSString *)serviceName;
+ (nullable NSArray<NSDictionary<NSString *, id> *> *)accountsForService:(nullable NSString *)serviceName error:(NSError *__autoreleasing *)error __attribute__((swift_error(none)));
#pragma mark - Configuration
#if __IPHONE_4_0 && TARGET_OS_IPHONE
/**
Returns the accessibility type for all future passwords saved to the Keychain.
@return Returns the accessibility type.
The return value will be `NULL` or one of the "Keychain Item Accessibility
Constants" used for determining when a keychain item should be readable.
@see setAccessibilityType
*/
+ (CFTypeRef)accessibilityType;
/**
Sets the accessibility type for all future passwords saved to the Keychain.
@param accessibilityType One of the "Keychain Item Accessibility Constants"
used for determining when a keychain item should be readable.
If the value is `NULL` (the default), the Keychain default will be used which
is highly insecure. You really should use at least `kSecAttrAccessibleAfterFirstUnlock`
for background applications or `kSecAttrAccessibleWhenUnlocked` for all
other applications.
@see accessibilityType
*/
+ (void)setAccessibilityType:(CFTypeRef)accessibilityType;
#endif
@end
NS_ASSUME_NONNULL_END
#import "SAMKeychainQuery.h"
文件二:
//
// SAMKeychain.m
// SAMKeychain
//
//
#import "SAMKeychain.h"
#import "SAMKeychainQuery.h"
NSString *const kSAMKeychainErrorDomain = @"com.samsoffes.samkeychain";
NSString *const kSAMKeychainAccountKey = @"acct";
NSString *const kSAMKeychainCreatedAtKey = @"cdat";
NSString *const kSAMKeychainClassKey = @"labl";
NSString *const kSAMKeychainDescriptionKey = @"desc";
NSString *const kSAMKeychainLabelKey = @"labl";
NSString *const kSAMKeychainLastModifiedKey = @"mdat";
NSString *const kSAMKeychainWhereKey = @"svce";
#if __IPHONE_4_0 && TARGET_OS_IPHONE
static CFTypeRef SAMKeychainAccessibilityType = NULL;
#endif
@implementation SAMKeychain
+ (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account {
return [self passwordForService:serviceName account:account error:nil];
}
+ (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error {
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = serviceName;
query.account = account;
[query fetch:error];
return query.password;
}
+ (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account {
return [self passwordDataForService:serviceName account:account error:nil];
}
+ (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error {
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = serviceName;
query.account = account;
[query fetch:error];
return query.passwordData;
}
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account {
return [self deletePasswordForService:serviceName account:account error:nil];
}
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error {
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = serviceName;
query.account = account;
return [query deleteItem:error];
}
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account {
return [self setPassword:password forService:serviceName account:account error:nil];
}
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error {
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = serviceName;
query.account = account;
query.password = password;
return [query save:error];
}
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account {
return [self setPasswordData:password forService:serviceName account:account error:nil];
}
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error {
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = serviceName;
query.account = account;
query.passwordData = password;
return [query save:error];
}
+ (nullable NSArray *)allAccounts {
return [self allAccounts:nil];
}
+ (nullable NSArray *)allAccounts:(NSError *__autoreleasing *)error {
return [self accountsForService:nil error:error];
}
+ (nullable NSArray *)accountsForService:(nullable NSString *)serviceName {
return [self accountsForService:serviceName error:nil];
}
+ (nullable NSArray *)accountsForService:(nullable NSString *)serviceName error:(NSError *__autoreleasing *)error {
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = serviceName;
return [query fetchAll:error];
}
#if __IPHONE_4_0 && TARGET_OS_IPHONE
+ (CFTypeRef)accessibilityType {
return SAMKeychainAccessibilityType;
}
+ (void)setAccessibilityType:(CFTypeRef)accessibilityType {
CFRetain(accessibilityType);
if (SAMKeychainAccessibilityType) {
CFRelease(SAMKeychainAccessibilityType);
}
SAMKeychainAccessibilityType = accessibilityType;
}
#endif
@end
文件三:
//
// SAMKeychainQuery.h
// SAMKeychain
//
//
#if __has_feature(modules)
@import Foundation;
@import Security;
#else
#import <Foundation/Foundation.h>
#import <Security/Security.h>
#endif
NS_ASSUME_NONNULL_BEGIN
#if __IPHONE_7_0 || __MAC_10_9
// Keychain synchronization available at compile time
#define SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE 1
#endif
#if __IPHONE_3_0 || __MAC_10_9
// Keychain access group available at compile time
#define SAMKEYCHAIN_ACCESS_GROUP_AVAILABLE 1
#endif
#ifdef SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE
typedef NS_ENUM(NSUInteger, SAMKeychainQuerySynchronizationMode) {
SAMKeychainQuerySynchronizationModeAny,
SAMKeychainQuerySynchronizationModeNo,
SAMKeychainQuerySynchronizationModeYes
};
#endif
/**
Simple interface for querying or modifying keychain items.
*/
@interface SAMKeychainQuery : NSObject
/** kSecAttrAccount */
@property (nonatomic, copy, nullable) NSString *account;
/** kSecAttrService */
@property (nonatomic, copy, nullable) NSString *service;
/** kSecAttrLabel */
@property (nonatomic, copy, nullable) NSString *label;
#ifdef SAMKEYCHAIN_ACCESS_GROUP_AVAILABLE
/** kSecAttrAccessGroup (only used on iOS) */
@property (nonatomic, copy, nullable) NSString *accessGroup;
#endif
#ifdef SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE
/** kSecAttrSynchronizable */
@property (nonatomic) SAMKeychainQuerySynchronizationMode synchronizationMode;
#endif
/** Root storage for password information */
@property (nonatomic, copy, nullable) NSData *passwordData;
/**
This property automatically transitions between an object and the value of
`passwordData` using NSKeyedArchiver and NSKeyedUnarchiver.
*/
@property (nonatomic, copy, nullable) id<NSCoding> passwordObject;
/**
Convenience accessor for setting and getting a password string. Passes through
to `passwordData` using UTF-8 string encoding.
*/
@property (nonatomic, copy, nullable) NSString *password;
///------------------------
/// @name Saving & Deleting
///------------------------
/**
Save the receiver's attributes as a keychain item. Existing items with the
given account, service, and access group will first be deleted.
@param error Populated should an error occur.
@return `YES` if saving was successful, `NO` otherwise.
*/
- (BOOL)save:(NSError **)error;
/**
Delete keychain items that match the given account, service, and access group.
@param error Populated should an error occur.
@return `YES` if saving was successful, `NO` otherwise.
*/
- (BOOL)deleteItem:(NSError **)error;
///---------------
/// @name Fetching
///---------------
/**
Fetch all keychain items that match the given account, service, and access
group. The values of `password` and `passwordData` are ignored when fetching.
@param error Populated should an error occur.
@return An array of dictionaries that represent all matching keychain items or
`nil` should an error occur.
The order of the items is not determined.
*/
- (nullable NSArray<NSDictionary<NSString *, id> *> *)fetchAll:(NSError **)error;
/**
Fetch the keychain item that matches the given account, service, and access
group. The `password` and `passwordData` properties will be populated unless
an error occurs. The values of `password` and `passwordData` are ignored when
fetching.
@param error Populated should an error occur.
@return `YES` if fetching was successful, `NO` otherwise.
*/
- (BOOL)fetch:(NSError **)error;
///-----------------------------
/// @name Synchronization Status
///-----------------------------
#ifdef SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE
/**
Returns a boolean indicating if keychain synchronization is available on the device at runtime. The #define
SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE is only for compile time. If you are checking for the presence of synchronization,
you should use this method.
@return A value indicating if keychain synchronization is available
*/
+ (BOOL)isSynchronizationAvailable;
#endif
@end
NS_ASSUME_NONNULL_END
文件四:
//
// SAMKeychainQuery.m
// SAMKeychain
//
//
#import "SAMKeychainQuery.h"
#import "SAMKeychain.h"
@implementation SAMKeychainQuery
@synthesize account = _account;
@synthesize service = _service;
@synthesize label = _label;
@synthesize passwordData = _passwordData;
#ifdef SAMKEYCHAIN_ACCESS_GROUP_AVAILABLE
@synthesize accessGroup = _accessGroup;
#endif
#ifdef SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE
@synthesize synchronizationMode = _synchronizationMode;
#endif
#pragma mark - Public
- (BOOL)save:(NSError *__autoreleasing *)error {
OSStatus status = SAMKeychainErrorBadArguments;
if (!self.service || !self.account || !self.passwordData) {
if (error) {
*error = [[self class] errorWithCode:status];
}
return NO;
}
NSMutableDictionary *query = nil;
NSMutableDictionary * searchQuery = [self query];
status = SecItemCopyMatching((__bridge CFDictionaryRef)searchQuery, nil);
if (status == errSecSuccess) {//item already exists, update it!
query = [[NSMutableDictionary alloc]init];
[query setObject:self.passwordData forKey:(__bridge id)kSecValueData];
#if __IPHONE_4_0 && TARGET_OS_IPHONE
CFTypeRef accessibilityType = [SAMKeychain accessibilityType];
if (accessibilityType) {
[query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible];
}
#endif
status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query));
}else if(status == errSecItemNotFound){//item not found, create it!
query = [self query];
if (self.label) {
[query setObject:self.label forKey:(__bridge id)kSecAttrLabel];
}
[query setObject:self.passwordData forKey:(__bridge id)kSecValueData];
#if __IPHONE_4_0 && TARGET_OS_IPHONE
CFTypeRef accessibilityType = [SAMKeychain accessibilityType];
if (accessibilityType) {
[query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible];
}
#endif
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
}
if (status != errSecSuccess && error != NULL) {
*error = [[self class] errorWithCode:status];
}
return (status == errSecSuccess);}
- (BOOL)deleteItem:(NSError *__autoreleasing *)error {
OSStatus status = SAMKeychainErrorBadArguments;
if (!self.service || !self.account) {
if (error) {
*error = [[self class] errorWithCode:status];
}
return NO;
}
NSMutableDictionary *query = [self query];
#if TARGET_OS_IPHONE
status = SecItemDelete((__bridge CFDictionaryRef)query);
#else
// On Mac OS, SecItemDelete will not delete a key created in a different
// app, nor in a different version of the same app.
//
// To replicate the issue, save a password, change to the code and
// rebuild the app, and then attempt to delete that password.
//
// This was true in OS X 10.6 and probably later versions as well.
//
// Work around it by using SecItemCopyMatching and SecKeychainItemDelete.
CFTypeRef result = NULL;
[query setObject:@YES forKey:(__bridge id)kSecReturnRef];
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
if (status == errSecSuccess) {
status = SecKeychainItemDelete((SecKeychainItemRef)result);
CFRelease(result);
}
#endif
if (status != errSecSuccess && error != NULL) {
*error = [[self class] errorWithCode:status];
}
return (status == errSecSuccess);
}
- (nullable NSArray *)fetchAll:(NSError *__autoreleasing *)error {
NSMutableDictionary *query = [self query];
[query setObject:@YES forKey:(__bridge id)kSecReturnAttributes];
[query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
#if __IPHONE_4_0 && TARGET_OS_IPHONE
CFTypeRef accessibilityType = [SAMKeychain accessibilityType];
if (accessibilityType) {
[query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible];
}
#endif
CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
if (status != errSecSuccess && error != NULL) {
*error = [[self class] errorWithCode:status];
return nil;
}
return (__bridge_transfer NSArray *)result;
}
- (BOOL)fetch:(NSError *__autoreleasing *)error {
OSStatus status = SAMKeychainErrorBadArguments;
if (!self.service || !self.account) {
if (error) {
*error = [[self class] errorWithCode:status];
}
return NO;
}
CFTypeRef result = NULL;
NSMutableDictionary *query = [self query];
[query setObject:@YES forKey:(__bridge id)kSecReturnData];
[query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
if (status != errSecSuccess) {
if (error) {
*error = [[self class] errorWithCode:status];
}
return NO;
}
self.passwordData = (__bridge_transfer NSData *)result;
return YES;
}
#pragma mark - Accessors
- (void)setPasswordObject:(id<NSCoding>)object {
self.passwordData = [NSKeyedArchiver archivedDataWithRootObject:object];
}
- (id<NSCoding>)passwordObject {
if ([self.passwordData length]) {
return [NSKeyedUnarchiver unarchiveObjectWithData:self.passwordData];
}
return nil;
}
- (void)setPassword:(NSString *)password {
self.passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
}
- (NSString *)password {
if ([self.passwordData length]) {
return [[NSString alloc] initWithData:self.passwordData encoding:NSUTF8StringEncoding];
}
return nil;
}
#pragma mark - Synchronization Status
#ifdef SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE
+ (BOOL)isSynchronizationAvailable {
#if TARGET_OS_IPHONE
// Apple suggested way to check for 7.0 at runtime
// https://developer.apple.com/library/ios/documentation/userexperience/conceptual/transitionguide/SupportingEarlieriOS.html
return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1;
#else
return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4;
#endif
}
#endif
#pragma mark - Private
- (NSMutableDictionary *)query {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3];
[dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
if (self.service) {
[dictionary setObject:self.service forKey:(__bridge id)kSecAttrService];
}
if (self.account) {
[dictionary setObject:self.account forKey:(__bridge id)kSecAttrAccount];
}
#ifdef SAMKEYCHAIN_ACCESS_GROUP_AVAILABLE
#if !TARGET_IPHONE_SIMULATOR
if (self.accessGroup) {
[dictionary setObject:self.accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
}
#endif
#endif
#ifdef SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE
if ([[self class] isSynchronizationAvailable]) {
id value;
switch (self.synchronizationMode) {
case SAMKeychainQuerySynchronizationModeNo: {
value = @NO;
break;
}
case SAMKeychainQuerySynchronizationModeYes: {
value = @YES;
break;
}
case SAMKeychainQuerySynchronizationModeAny: {
value = (__bridge id)(kSecAttrSynchronizableAny);
break;
}
}
[dictionary setObject:value forKey:(__bridge id)(kSecAttrSynchronizable)];
}
#endif
return dictionary;
}
+ (NSError *)errorWithCode:(OSStatus) code {
static dispatch_once_t onceToken;
static NSBundle *resourcesBundle = nil;
dispatch_once(&onceToken, ^{
NSURL *url = [[NSBundle bundleForClass:[SAMKeychainQuery class]] URLForResource:@"SAMKeychain" withExtension:@"bundle"];
resourcesBundle = [NSBundle bundleWithURL:url];
});
NSString *message = nil;
switch (code) {
case errSecSuccess: return nil;
case SAMKeychainErrorBadArguments: message = NSLocalizedStringFromTableInBundle(@"SAMKeychainErrorBadArguments", @"SAMKeychain", resourcesBundle, nil); break;
#if TARGET_OS_IPHONE
case errSecUnimplemented: {
message = NSLocalizedStringFromTableInBundle(@"errSecUnimplemented", @"SAMKeychain", resourcesBundle, nil);
break;
}
case errSecParam: {
message = NSLocalizedStringFromTableInBundle(@"errSecParam", @"SAMKeychain", resourcesBundle, nil);
break;
}
case errSecAllocate: {
message = NSLocalizedStringFromTableInBundle(@"errSecAllocate", @"SAMKeychain", resourcesBundle, nil);
break;
}
case errSecNotAvailable: {
message = NSLocalizedStringFromTableInBundle(@"errSecNotAvailable", @"SAMKeychain", resourcesBundle, nil);
break;
}
case errSecDuplicateItem: {
message = NSLocalizedStringFromTableInBundle(@"errSecDuplicateItem", @"SAMKeychain", resourcesBundle, nil);
break;
}
case errSecItemNotFound: {
message = NSLocalizedStringFromTableInBundle(@"errSecItemNotFound", @"SAMKeychain", resourcesBundle, nil);
break;
}
case errSecInteractionNotAllowed: {
message = NSLocalizedStringFromTableInBundle(@"errSecInteractionNotAllowed", @"SAMKeychain", resourcesBundle, nil);
break;
}
case errSecDecode: {
message = NSLocalizedStringFromTableInBundle(@"errSecDecode", @"SAMKeychain", resourcesBundle, nil);
break;
}
case errSecAuthFailed: {
message = NSLocalizedStringFromTableInBundle(@"errSecAuthFailed", @"SAMKeychain", resourcesBundle, nil);
break;
}
default: {
message = NSLocalizedStringFromTableInBundle(@"errSecDefault", @"SAMKeychain", resourcesBundle, nil);
}
#else
default:
message = (__bridge_transfer NSString *)SecCopyErrorMessageString(code, NULL);
#endif
}
NSDictionary *userInfo = nil;
if (message) {
userInfo = @{ NSLocalizedDescriptionKey : message };
}
return [NSError errorWithDomain:kSAMKeychainErrorDomain code:code userInfo:userInfo];
}
@end
最后调用,我这里是取idfa的值存到(keyChain)钥匙串中,要是idfa取不到,就把idfv存到(keyChain)钥匙串中,代码如下
第一步:
#import "SAMKeychain.h"
#import "SAMKeychainQuery.h"
第二步:
NSString *forService = @"com.test-yqj.www";
NSString *forAccount = @"deviceUid";
NSError *passwordForServiceError = nil;
NSError *setPasswordError = nil;
NSString *passwordForService = [SAMKeychain passwordForService: forService account: forAccount error: &passwordForServiceError];
NSString *deviceUid = @"";
NSString *defaultType = @"";
if (passwordForService == nil) {
// idfa 00000000-0000-0000-0000-000000000000
NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
NSString *defaultIdfa = @"00000000-0000-0000-0000-000000000000";
if ([defaultIdfa isEqualToString:idfa]) {
//idfv
NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
deviceUid = idfv;
defaultType = @"idfv";
} else{
deviceUid = idfa;
defaultType = @"idfa";
}
[SAMKeychain setPassword: deviceUid forService: forService account: forAccount error: &setPasswordError];
} else {
deviceUid = passwordForService;
defaultType = @"keyChain";
}
上图中注释的00000000-0000-0000-0000-000000000000是当idfa取不到的时候系统返回的默认字符串类型初始值,当然还可以更简化,主要看业务需求
下面是四个文件的方法介绍
+ (NSArray *)allAccounts; //获取所有账户
+ (NSArray *)accountsForService:(NSString *)serviceName; //获取所有服务名
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account; //获取对应服务名的账户
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account; //删除对应服务名的账户的密码
+ (void)setAccessibilityType:(CFTypeRef)accessibilityType; //设置类型
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account; //设置对应服务名的账户的密码
调试方法
NSError *error = nil;
SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
query.service = @"MyService";
query.account = @"soffes";
[query fetch:&error];
if ([error code] == errSecItemNotFound) {
NSLog(@"Password not found");
} else if (error != nil) {
NSLog(@"Some other error occurred: %@", [error localizedDescription]);
}
okay,结束,剩余时间去看动作大片吧哈