Keychain的使用:KeychainTouchIDUsingTouchIDwithKeychainandLocalAuthentication

苹果官方文档地址


keychain保护

/* 设置keychain项保护等级 */

NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,

(__bridge id)kSecAttrGeneric:@"MyItem",

(__bridge id)kSecAttrAccount:@"username",

(__bridge id)kSecValueData:@"password",

(__bridge id)kSecAttrService:[NSBundle mainBundle].bundleIdentifier,

(__bridge id)kSecAttrLabel:@"",

(__bridge id)kSecAttrDescription:@"",

(__bridge id)kSecAttrAccessible:(__bridge id)kSecAttrAccessibleWhenUnlocked};

OSStatus result = SecItemAdd((__bridge CFDictionaryRef)(query), NULL);

//keychain项保护等级列表

kSecAttrAccessibleWhenUnlocked                          //keychain项受到保护,只有在设备未被锁定时才可以访问

kSecAttrAccessibleAfterFirstUnlock                      //keychain项受到保护,直到设备启动并且用户第一次输入密码

kSecAttrAccessibleAlways                                //keychain未受保护,任何时候都可以访问 (Default)

kSecAttrAccessibleWhenUnlockedThisDeviceOnly            //keychain项受到保护,只有在设备未被锁定时才可以访问,而且不可以转移到其他设备

kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly        //keychain项受到保护,直到设备启动并且用户第一次输入密码,而且不可以转移到其他设备

kSecAttrAccessibleAlwaysThisDeviceOnly                  //keychain未受保护,任何时候都可以访问,但是不能转移到其他设备

http://www.cnblogs.com/ios8/p/ios-data-protect.html


iOS钥匙串KeyChain相关参数的说明

keychain(二)

//以上为参考文章

KeychainTouchID官方DEMO


增删改查的时候:kSecAttrService:的值保持一致

(id)kSecValueData: 内容(要保存的信息)

/*

Copyright (C) 2016 Apple Inc. All Rights Reserved.

See LICENSE.txt for this sample’s licensing information

Abstract:

Keychain with Touch ID demo implementation.

*/

#import "AAPLKeychainTestsViewController.h"

#import "AAPLTest.h"

@import Security;

@import LocalAuthentication;

@implementation AAPLKeychainTestsViewController

- (void)viewDidLoad {

[super viewDidLoad];

// prepare the actions which can be tested in this class

self.tests = @[

[[AAPLTest alloc] initWithName:@"Add item" details:@"Using SecItemAdd()" selector:@selector(addItemAsync)],

[[AAPLTest alloc] initWithName:@"Add item (TouchID only)" details:@"Using SecItemAdd()" selector:@selector(addTouchIDItemAsync)],

[[AAPLTest alloc] initWithName:@"Add item (TouchID and password)" details:@"Using SecItemAdd()" selector:@selector(addPwdItem)],

[[AAPLTest alloc] initWithName:@"Query for item" details:@"Using SecItemCopyMatching()" selector:@selector(copyMatchingAsync)],

[[AAPLTest alloc] initWithName:@"Update item" details:@"Using SecItemUpdate()" selector:@selector(updateItemAsync)],

[[AAPLTest alloc] initWithName:@"Delete item" details:@"Using SecItemDelete()" selector:@selector(deleteItemAsync)],

[[AAPLTest alloc] initWithName:@"Add protected key" details:@"Using SecKeyCreateRandomKey()" selector:@selector(generateKeyAsync)],

[[AAPLTest alloc] initWithName:@"Use protected key" details:@"Using SecKeyCreateSignature()" selector:@selector(useKeyAsync)],

[[AAPLTest alloc] initWithName:@"Delete protected key" details:@"Using SecItemDelete()" selector:@selector(deleteKeyAsync)]

];

}

- (void)viewWillAppear:(BOOL)animated {

[super viewWillAppear:animated];

[self.textView scrollRangeToVisible:NSMakeRange(self.textView.text.length, 0)];

}

- (void)viewDidLayoutSubviews {

// Set the proper size for the table view based on its content.

CGFloat height = MIN(self.view.bounds.size.height, self.tableView.contentSize.height);

self.dynamicViewHeight.constant = height;

[self.view layoutIfNeeded];

}

#pragma mark - Tests

//查

- (void)copyMatchingAsync {

NSDictionary *query = @{

(id)kSecClass: (id)kSecClassGenericPassword,

(id)kSecAttrService: @"SampleService",

(id)kSecReturnData: @YES,

(id)kSecUseOperationPrompt: @"Authenticate to access service password",

};

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

CFTypeRef dataTypeRef = NULL;

NSString *message;

OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)(query), &dataTypeRef);

if (status == errSecSuccess) {

NSData *resultData = (__bridge_transfer NSData *)dataTypeRef;

NSString *result = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];

message = [NSString stringWithFormat:@"Result: %@\n", result];

}

else {

message = [NSString stringWithFormat:@"SecItemCopyMatching status: %@", [self keychainErrorToString:status]];

}

[self printMessage:message inTextView:self.textView];

});

}

//更新

- (void)updateItemAsync {

NSDictionary *query = @{

(id)kSecClass: (id)kSecClassGenericPassword,

(id)kSecAttrService: @"SampleService",

(id)kSecUseOperationPrompt: @"Authenticate to update your password"

};

NSData *updatedSecretPasswordTextData = [@"UPDATED_SECRET_PASSWORD_TEXT" dataUsingEncoding:NSUTF8StringEncoding];

NSDictionary *changes = @{

(id)kSecValueData: updatedSecretPasswordTextData

};

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)changes);

NSString *errorString = [self keychainErrorToString:status];

NSString *message = [NSString stringWithFormat:@"SecItemUpdate status: %@", errorString];

[super printMessage:message inTextView:self.textView];

});

}

//增加

- (void)addItemAsync {

CFErrorRef error = NULL;

// Should be the secret invalidated when passcode is removed? If not then use kSecAttrAccessibleWhenUnlocked

SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,

kSecAccessControlUserPresence, &error);

if (sacObject == NULL || error != NULL) {

NSString *errorString = [NSString stringWithFormat:@"SecItemAdd can't create sacObject: %@", error];

self.textView.text = [self.textView.text stringByAppendingString:errorString];

return;

}

// we want the operation to fail if there is an item which needs authentication so we will use

// kSecUseNoAuthenticationUI //(__bridge id)kSecAttrSynchronizable : @YES,

NSDictionary *attributes = @{

(id)kSecClass: (id)kSecClassGenericPassword,

(id)kSecAttrService: @"SampleService",

(id)kSecValueData: [@"password-Or-account" dataUsingEncoding:NSUTF8StringEncoding],

(id)kSecUseAuthenticationUI: (id)kSecUseAuthenticationUIAllow,

(id)kSecAttrAccessControl: (__bridge_transfer id)sacObject

};

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

OSStatus status =  SecItemAdd((__bridge CFDictionaryRef)attributes, nil);

NSString *errorString = [self keychainErrorToString:status];

NSString *message = [NSString stringWithFormat:@"SecItemAdd status: %@", errorString];

[self printMessage:message inTextView:self.textView];

});

}

- (void)addTouchIDItemAsync {

CFErrorRef error = NULL;

// Should be the secret invalidated when passcode is removed? If not then use kSecAttrAccessibleWhenUnlocked

SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,

kSecAccessControlTouchIDAny, &error);

if (sacObject == NULL || error != NULL) {

NSString *errorString = [NSString stringWithFormat:@"SecItemAdd can't create sacObject: %@", error];

self.textView.text = [self.textView.text stringByAppendingString:errorString];

return;

}

/*

We want the operation to fail if there is an item which needs authentication so we will use

`kSecUseNoAuthenticationUI`.

*/

NSData *secretPasswordTextData = [@"SECRET_PASSWORD_TEXT" dataUsingEncoding:NSUTF8StringEncoding];

NSDictionary *attributes = @{

(id)kSecClass: (id)kSecClassGenericPassword,

(id)kSecAttrService: @"SampleService",

(id)kSecValueData: secretPasswordTextData,

(id)kSecUseAuthenticationUI: (id)kSecUseAuthenticationUIAllow,

(id)kSecAttrAccessControl: (__bridge_transfer id)sacObject

};

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

OSStatus status =  SecItemAdd((__bridge CFDictionaryRef)attributes, nil);

NSString *message = [NSString stringWithFormat:@"SecItemAdd status: %@", [self keychainErrorToString:status]];

[self printMessage:message inTextView:self.textView];

});

}

- (void)addPwdItem {

CFErrorRef error = NULL;

// Should be the secret invalidated when passcode is removed? If not then use kSecAttrAccessibleWhenUnlocke.

SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,

kSecAccessControlTouchIDAny | kSecAccessControlApplicationPassword, &error);

if (sacObject == NULL || error != NULL) {

NSString *errorString = [NSString stringWithFormat:@"SecItemAdd can't create sacObject: %@", error];

self.textView.text = [self.textView.text stringByAppendingString:errorString];

return;

}

LAContext *context = [[LAContext alloc] init];

[context evaluateAccessControl:sacObject operation:LAAccessControlOperationCreateItem localizedReason:@"Create Item" reply:^(BOOL success, NSError * error) {

if (success) {

/*

We want the operation to fail if there is an item which needs authentication so we will use

`kSecUseNoAuthenticationUI`.

*/

NSData *secretPasswordTextData = [@"SECRET_PASSWORD_TEXT" dataUsingEncoding:NSUTF8StringEncoding];

NSDictionary *attributes = @{

(id)kSecClass: (id)kSecClassGenericPassword,

(id)kSecAttrService: @"SampleService",

(id)kSecValueData: secretPasswordTextData,

(id)kSecUseAuthenticationUI: (id)kSecUseAuthenticationUIAllow,

(id)kSecAttrAccessControl: (__bridge_transfer id)sacObject,

(id)kSecUseAuthenticationContext: context

};

OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, nil);

NSString *error = [self keychainErrorToString:status];

NSString *message = [NSString stringWithFormat:@"SecItemAdd status: %@", error];

[self printMessage:message inTextView:self.textView];

}

else {

[self printMessage:error.description inTextView:self.textView];

CFRelease(sacObject);

}

}];

}

- (void)deleteItemAsync {

NSDictionary *query = @{

(id)kSecClass: (id)kSecClassGenericPassword,

(id)kSecAttrService: @"SampleService"

};

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);

NSString *errorString = [self keychainErrorToString:status];

NSString *message = [NSString stringWithFormat:@"SecItemDelete status: %@", errorString];

[super printMessage:message inTextView:self.textView];

});

}

- (void)generateKeyAsync {

CFErrorRef error = NULL;

SecAccessControlRef sacObject;

// Should be the secret invalidated when passcode is removed? If not then use `kSecAttrAccessibleWhenUnlocked`.

sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,

kSecAccessControlTouchIDAny | kSecAccessControlPrivateKeyUsage, &error);

// Create parameters dictionary for key generation.

NSDictionary *parameters = @{

(id)kSecAttrTokenID: (id)kSecAttrTokenIDSecureEnclave,

(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,

(id)kSecAttrKeySizeInBits: @256,

(id)kSecAttrLabel: @"my-se-key",

(id)kSecPrivateKeyAttrs: @{

(id)kSecAttrAccessControl: (__bridge_transfer id)sacObject,

(id)kSecAttrIsPermanent: @YES,

}

};

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// Generate key pair.

NSError *gen_error = nil;

id privateKey = CFBridgingRelease(SecKeyCreateRandomKey((__bridge CFDictionaryRef)parameters, (void *)&gen_error));

if(privateKey != nil) {

// use the private key in your code

[self printMessage:@"Key generation success" inTextView:self.textView];

}

else {

NSString *message = [NSString stringWithFormat:@"Key generation error: %@", gen_error];

[self printMessage:message inTextView:self.textView];

}

});

}

- (void)useKeyAsync {

// Query private key object from the keychain.

NSDictionary *params = @{

(id)kSecClass: (id)kSecClassKey,

(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,

(id)kSecAttrKeySizeInBits: @256,

(id)kSecAttrLabel: @"my-se-key",

(id)kSecReturnRef: @YES,

(id)kSecUseOperationPrompt: @"Authenticate to sign data"

};

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// Retrieve the key from the keychain.  No authentication is needed at this point.

SecKeyRef privateKey;

OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)params, (CFTypeRef *)&privateKey);

if (status == errSecSuccess) {

NSError *error;

NSData *dataToSign = [@"message" dataUsingEncoding:NSUTF8StringEncoding];

NSData *signature = CFBridgingRelease(SecKeyCreateSignature(privateKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)dataToSign, (void *)&error));

NSString *message = [NSString stringWithFormat:@"Key used: %@", error];

[self printMessage:message inTextView:self.textView];

if (status == errSecSuccess) {

// In your own code, here is where you'd continue with the signature of the digest.

id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey));

Boolean verified = SecKeyVerifySignature((SecKeyRef)publicKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)dataToSign, (CFDataRef)signature, (void *)&error);

message = [NSString stringWithFormat:@"signature:%@ verified:%d error:%@", signature, verified, error];

[self printMessage:message inTextView:self.textView];

}

CFRelease(privateKey);

}

else {

NSString *message = [NSString stringWithFormat:@"Key not found: %@",[self keychainErrorToString:status]];

[self printMessage:message inTextView:self.textView];

}

});

}

- (void)deleteKeyAsync {

NSDictionary *query = @{

(id)kSecAttrTokenID: (id)kSecAttrTokenIDSecureEnclave,

(id)kSecClass: (id)kSecClassKey,

(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,

(id)kSecAttrLabel: @"my-se-key",

(id)kSecReturnRef: @YES,

};

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);

NSString *message = [NSString stringWithFormat:@"SecItemDelete status: %@", [self keychainErrorToString:status]];

[self printMessage:message inTextView:self.textView];

});

}

#pragma mark - Tools

- (NSString *)keychainErrorToString:(OSStatus)error {

NSString *message = [NSString stringWithFormat:@"%ld", (long)error];

switch (error) {

case errSecSuccess:

message = @"success";

break;

case errSecDuplicateItem:

message = @"error item already exists";

break;

case errSecItemNotFound :

message = @"error item not found";

break;

case errSecAuthFailed:

message = @"error item authentication failed";

break;

default:

break;

}

return message;

}

@end

你可能感兴趣的:(Keychain的使用:KeychainTouchIDUsingTouchIDwithKeychainandLocalAuthentication)