iOS 指纹识别/人脸识别登录(ECDSA 加签)

生物识别的基本逻辑:
1.需要在plist文件中添加 Privacy-Face ID Usage Description 属性
为了方便您快速登录,XXXXX需要访问您的面容 ID/指纹ID

2.获取授权
TDTouchID.h

#import <LocalAuthentication/LocalAuthentication.h>

/**
 *  设备支持的生物验证方式
 */
typedef enum : NSUInteger {
    /**
     *  支持TouchID验证
     */
    TDTouchIDSupperTypeTouchID = 1,
    /**
     *  支持FaceID验证
     */
    TDTouchIDSupperTypeFaceID,
    /**
     *  不支持支持验证
     */
    TDTouchIDSupperTypeNone,
} TDTouchIDSupperType;


/**
 *  TouchID 状态
 */
typedef enum : NSUInteger {
    /**
     *  当前设备不支持生物验证
     */
    TDTouchIDStateNotSupport = 1,
    /**
     *  生物验证 验证成功
     */
    TDTouchIDStateSuccess,
    /**
     *  生物验证 验证失败
     */
    TDTouchIDStateFail,
    /**
     *  生物验证 被用户手动取消
     */
    TDTouchIDStateUserCancel,
    /**
     *  用户不使用生物验证,选择手动输入密码
     */
    TDTouchIDStateInputPassword,
    /**
     *  生物验证 被系统取消 (如遇到来电,锁屏,按了Home键等)
     */
    TDTouchIDStateSystemCancel,
    /**
     *  生物验证 无法启动,因为用户没有设置密码
     */
    TDTouchIDStatePasswordNotSet,
    /**
     *  生物验证 无法启动,因为用户没有设置生物验证
     */
    TDTouchIDStateTouchIDNotSet,
    /**
     *  生物验证 无效
     */
    TDTouchIDStateTouchIDNotAvailable,
    /**
     *  生物验证 被锁定(连续多次验证生物验证失败,系统需要用户手动输入密码)
     */
    TDTouchIDStateTouchIDLockout,
    /**
     *  当前软件被挂起并取消了授权 (如App进入了后台等)
     */
    TDTouchIDStateAppCancel,
    /**
     *  当前软件被挂起并取消了授权 (LAContext对象无效)
     */
    TDTouchIDStateInvalidContext,
    /**
     *  系统版本不支持生物验证 (必须高于iOS 8.0才能使用)
     */
    TDTouchIDStateVersionNotSupport,
    /**
     *  输入密码成功
     */
    TDTouchIDStateInputPasswordSuccess,
    /**
     *  输入密码失败
     */
    TDTouchIDStateInputPasswordFail,
    /**
     *  未注册
     */
    TDTouchIDStateNotEnrolled,
   
} TDTouchIDState;

@interface TDTouchID : LAContext

typedef void (^StateBlock)(TDTouchIDState state,NSError *error);

+ (instancetype)sharedInstance;

/**
 启动生物验证

 @param desc Touch显示的描述
 @param block 回调状态的block
 */
- (void)td_showTouchIDWithDescribe:(NSString *)desc BlockState:(StateBlock)block;

/**
 启动生物验证
 @param desc Touch显示的描述
 @param faceDesc FaceID状态下显示的描述
 @param block 回调状态的block
 */
- (void)td_showTouchIDWithDescribe:(NSString *)desc FaceIDDescribe:(NSString *)faceDesc BlockState:(StateBlock)block;

// 判断设备支持哪种认证方式 TouchID & FaceID
- (TDTouchIDSupperType)td_canSupperBiometrics;


@end

TDTouchID.m

#import "TDTouchID.h"

@implementation TDTouchID

+ (instancetype)sharedInstance {
    static TDTouchID *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[TDTouchID alloc] init];
    });
    return instance;
}

- (void)td_showTouchIDWithDescribe:(NSString *)desc FaceIDDescribe:(NSString *)faceDesc BlockState:(StateBlock)block {
    
    TDTouchIDSupperType supperType = [self td_canSupperBiometrics];
    
    NSString *descStr;
//    if (supperType == TDTouchIDSupperTypeTouchID && desc.length == 0) {
//        descStr = @"通过Home键验证已有指纹";
//    }else{
//        descStr = desc;
//    }
//    if (supperType == TDTouchIDSupperTypeFaceID && faceDesc.length == 0) {
//        descStr = @"通过已有面容ID验证";
//    }else{
//        descStr = faceDesc;
//    }
    if (supperType == TDTouchIDSupperTypeTouchID ) {
        descStr = @"通过Home键验证已有指纹";
    }else if (supperType == TDTouchIDSupperTypeFaceID) {
        descStr = @"通过已有面容ID验证";
    }else{
        descStr = descStr;
    }
    if (NSFoundationVersionNumber < NSFoundationVersionNumber_iOS_8_0) {
        
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"系统版本不支持TouchID (必须高于iOS 8.0才能使用)");
            block(TDTouchIDStateVersionNotSupport,nil);
        });
        
        return;
    }
    
    LAContext *context = [[LAContext alloc] init];
    
    context.localizedFallbackTitle = @"输入密码";

    NSError *error = nil;
  
    
    if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
        
        [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:descStr reply:^(BOOL success, NSError * _Nullable error) {
            
            if (success) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"TouchID 验证成功");
                    block(TDTouchIDStateSuccess,error);
                });
            }else if(error){
                
                switch (error.code) {
                    case LAErrorAuthenticationFailed:{
                        dispatch_async(dispatch_get_main_queue(), ^{
                            NSLog(@"TouchID 验证失败");
                            block(TDTouchIDStateFail,error);
                        });
                        break;
                    }
                    case LAErrorUserCancel:{
                        dispatch_async(dispatch_get_main_queue(), ^{
                            NSLog(@"TouchID 被用户手动取消");
                            block(TDTouchIDStateUserCancel,error);
                        });
                    }
                        break;
                    case LAErrorUserFallback:{
                        dispatch_async(dispatch_get_main_queue(), ^{
                            
                            NSInteger policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
                                  if ([UIDevice currentDevice].systemVersion.floatValue > 9.0) {
                                      policy = LAPolicyDeviceOwnerAuthentication;
                                  }
                            
                            [context evaluatePolicy:policy localizedReason:@" " reply:^(BOOL success, NSError * _Nullable error) {
                                if (success) {
                                    NSLog(@"TDTouchIDStateInputPasswordSuccess,选择手动输入密码");

                                    block(TDTouchIDStateInputPasswordSuccess,error);
                                }else{
                                    NSLog(@"用户不使用TouchID,选择手动输入密码");
                                    
                                    switch (error.code) {
                                        case LAErrorUserCancel:
                                            NSLog(@"用户取消");
                                            block(TDTouchIDStateUserCancel,error);

                                            break;
                                       
                                        case LAErrorPasscodeNotSet:
                                            NSLog(@"没有设置密码");
                                            block(TDTouchIDStatePasswordNotSet,error);
                                            break;
                                        default:
                                            block(TDTouchIDStateInputPasswordFail,error);

                                            break;
                                    }
                                    

                                }
                            }];
                                
                        });
                          
                            
                            
                            
                            
//                        LAContext *context = [[LAContext alloc]init];
//                        [context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:@"输入密码" reply:^(BOOL success, NSError * _Nullable error) {
//                            if (success) {
//                                block(TDTouchIDStateInputPasswordSuccess,error);
//                            }else{
//                                block(TDTouchIDStateInputPasswordFail,error);
//                            }
//                        }];
//                            });
//                        block(TDTouchIDStateInputPassword,error);

                    }
                        
                        break;
                    case LAErrorSystemCancel:{
                        dispatch_async(dispatch_get_main_queue(), ^{
                            NSLog(@"TouchID 被系统取消 (如遇到来电,锁屏,按了Home键等)");
                            block(TDTouchIDStateSystemCancel,error);
                        });
                    }
                        break;
                    case LAErrorPasscodeNotSet:{
                        dispatch_async(dispatch_get_main_queue(), ^{
                            NSLog(@"TouchID 无法启动,因为用户没有设置密码");
                            block(TDTouchIDStatePasswordNotSet,error);
                        });
                    }
                        break;
                    case LAErrorTouchIDNotEnrolled:{
                        dispatch_async(dispatch_get_main_queue(), ^{
                            NSLog(@"TouchID 无法启动,因为用户没有设置TouchID");
                            block(TDTouchIDStateTouchIDNotSet,error);
                        });
                    }
                        break;
                    case LAErrorTouchIDNotAvailable:{
                        dispatch_async(dispatch_get_main_queue(), ^{
                            NSLog(@"TouchID 无效");
                            block(TDTouchIDStateTouchIDNotAvailable,error);
                        });
                    }
                        break;
                    case LAErrorTouchIDLockout:{
                        dispatch_async(dispatch_get_main_queue(), ^{
                            NSLog(@"TouchID 被锁定(连续多次验证TouchID失败,系统需要用户手动输入密码)");
                            block(TDTouchIDStateTouchIDLockout,error);
                        });
                    }
                        break;
                    case LAErrorAppCancel:{
                        dispatch_async(dispatch_get_main_queue(), ^{
                            NSLog(@"当前软件被挂起并取消了授权 (如App进入了后台等)");
                            block(TDTouchIDStateAppCancel,error);
                        });
                    }
                        break;
                    case LAErrorInvalidContext:{
                        dispatch_async(dispatch_get_main_queue(), ^{
                            NSLog(@"当前软件被挂起并取消了授权 (LAContext对象无效)");
                            block(TDTouchIDStateInvalidContext,error);
                        });
                    }
                        break;
                    default:
                        break;
                }
            }
        }];
        
    }else{
        NSLog(@"不支持指纹识别");
       switch (error.code) {
           case LAErrorBiometryNotEnrolled:
           {
               dispatch_async(dispatch_get_main_queue(), ^{
                   NSLog(@"TouchID is not enrolled TouchID未注册");
                   block(TDTouchIDStateTouchIDNotSet,error);
               });
               break;
           }
           case LAErrorPasscodeNotSet:
           {
               dispatch_async(dispatch_get_main_queue(), ^{
                   NSLog(@"A passcode has not been set 未设置密码");
                   block(TDTouchIDStatePasswordNotSet,error);
               });
               break;
           }
           case LAErrorTouchIDNotAvailable:{
               dispatch_async(dispatch_get_main_queue(), ^{
                   NSLog(@"TouchID 无效");
                   block(TDTouchIDStateTouchIDNotAvailable,error);
               });
           }
               break;
           case LAErrorTouchIDLockout:{
               dispatch_async(dispatch_get_main_queue(), ^{
                   NSLog(@"TouchID 被锁定(连续多次验证TouchID失败,系统需要用户手动输入密码)");
                   block(TDTouchIDStateTouchIDLockout,error);
               });
           }
               break;
           default:
           {
               dispatch_async(dispatch_get_main_queue(), ^{
                   NSLog(@"当前设备不支持TouchID");
                   block(TDTouchIDStateNotSupport,error);
               });
               break;
           }
       }
       
        
    }
    
}



- (void)td_showTouchIDWithDescribe:(NSString *)desc BlockState:(StateBlock)block{
    [self td_showTouchIDWithDescribe:desc FaceIDDescribe:nil BlockState:block];
}

// 判断设备支持哪种认证方式 TouchID & FaceID
- (TDTouchIDSupperType)td_canSupperBiometrics {
    LAContext *context = [[LAContext alloc] init];
    NSError *error;
    if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
        if (error != nil) {
            return TDTouchIDSupperTypeNone;
        }
        if (@available(iOS 11.0, *)) {
            return context.biometryType == LABiometryTypeTouchID ?  TDTouchIDSupperTypeTouchID:TDTouchIDSupperTypeFaceID ;
        }
    }
    return TDTouchIDSupperTypeNone;
}
@end

3.在需要的VC 进行调用

第一次设置指纹解锁/人脸识别解锁:
(1)获取授权成功之后,判断系统支持哪一种识别方式。
(2)验证成功之后,将随机生成的UUID + token + 公钥字符串 进行ECDSA签名;同时再把这个公钥字符串、token、UUID 传给后台,使该设备与用户绑定;绑定成功后可以将UUID及签名存下来
(3)登录的时候把UUID、签名过后的UUID传给后台 ,后台用公钥进行验签,验签成功即可登录账户。

第一次设置

#import <CommonCrypto/CommonDigest.h>
#import <LocalAuthentication/LAContext.h>
#import "TDTouchID.h"

#define Secp256r1CurveLen     256
unsigned char Secp256r1header[] =

{

    0x30, 0x59, 0x30, 0x13, 0x06, 0x07,

    0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,

    0x01, 0x06, 0x08, 0x2A, 0x86, 0x48,

    0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03,

    0x42, 0x00

};
#define Secp256r1headerLen    26




//随机生成UUID字符串
- (NSString *)uuidString
{
    CFUUIDRef uuid_ref = CFUUIDCreate(NULL);
    CFStringRef uuid_string_ref= CFUUIDCreateString(NULL, uuid_ref);
    NSString *uuid = [NSString stringWithString:(__bridge NSString *)uuid_string_ref];
    CFRelease(uuid_ref);
    CFRelease(uuid_string_ref);
    return [uuid lowercaseString];
}
//验证指纹识别/人脸识别
- (void)touchVerificationWithSwitch:(UISwitch *)btnSwitch {
    DefinitionWeak(self);
    if (btnSwitch.on == YES) {
        TDTouchIDSupperType type = [[TDTouchID sharedInstance] td_canSupperBiometrics];
      NSString * strTitle = (type==TDTouchIDSupperTypeFaceID?@"您尚未设置面容ID,请到设置中心设置您的面容ID":@"您尚未设置触控ID,请到设置中心设置您的触控ID");
        
        [[TDTouchID sharedInstance] td_showTouchIDWithDescribe:@"通过Home键验证已有指纹" FaceIDDescribe:@"通过已有面容ID验证" BlockState:^(TDTouchIDState state, NSError *error) {
            if (state == TDTouchIDStateNotSupport) {    //不支持TouchID/FaceID
                dispatch_async(dispatch_get_main_queue(), ^{
                [btnSwitch setOn:NO];
                [weak_self showMessage:@"当前设备不支持TouchID/FaceID"];

                });
            }else if (state == TDTouchIDStateSuccess) {    //TouchID/FaceID验证成功
                NSLog(@"jump");
                dispatch_async(dispatch_get_main_queue(), ^{
                    [btnSwitch setOn:YES];
                    [weak_self requestRelationInfo];
                });
            }else if (state == TDTouchIDStateInputPasswordSuccess) {//密码输入成功
                NSLog(@"jump");
                dispatch_async(dispatch_get_main_queue(), ^{
                    [btnSwitch setOn:YES];
                    [weak_self requestRelationInfo];
                });
            }else if (state == TDTouchIDStateInputPasswordFail) {//密码输入取消 或者错误
                dispatch_async(dispatch_get_main_queue(), ^{
                    [btnSwitch setOn:NO];
                    [weak_self showMessage:@"请稍后重试"];

                });

            } else if (state == TDTouchIDStateTouchIDNotSet) { // TouchID 无法启动,因为用户没有设置TouchID
                NSLog(@"jump");
                dispatch_async(dispatch_get_main_queue(), ^{
                [btnSwitch setOn:NO];
                });
                [weak_self showAlertWithTitle:@"提示" message:strTitle cancelTitle:@"取消" okTitle:@"确定" completion:^(BOOL cancelled) {
                    if (cancelled) {
                        NSLog(@"确定");
                        //跳转到设置中心
                        NSURL *url = [NSURL URLWithString:@"App-Prefs:root=TOUCHID_PASSCODE"];
                         if ( [[UIApplication sharedApplication] canOpenURL: url] ) {
                             NSURL*url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
                           if (@available(iOS 10.0, *)){
                               [[UIApplication sharedApplication]openURL:url options:@{} completionHandler:^(BOOL success) {
                                                              
                               }];
                             }else{
                                 [[UIApplication sharedApplication] openURL:url];
                             }
                             
                         }
                    };
                }];
            }else if (state == TDTouchIDStateInvalidContext) { //当前软件被挂起并取消了授权 (LAContext对象无效)")
                dispatch_async(dispatch_get_main_queue(), ^{
                [btnSwitch setOn:NO];
                });
                
            }else if (state == TDTouchIDStateAppCancel) { // 当前软件被挂起并取消了授权 (如App进入了后台等)
                NSLog(@"jump");
                dispatch_async(dispatch_get_main_queue(), ^{
                [btnSwitch setOn:NO];
                });
            }else if (state == TDTouchIDStateTouchIDLockout) { // TouchID 被锁定(连续多次验证TouchID失败,系统需要用户手动输入密码)
                dispatch_async(dispatch_get_main_queue(), ^{
                [btnSwitch setOn:NO];
                });
                NSString *strMessage =[NSString stringWithFormat:@"%@被锁定,请稍后重试",(type==TDTouchIDSupperTypeFaceID)?@"面容ID":@"触控ID"];
                [weak_self showAlertWithTitle:@"提示" message:strMessage cancelTitle:@"取消" okTitle:@"确定" completion:^(BOOL cancelled) {
                    if (cancelled) {
                        NSLog(@"确定");
                    };
                }];
            }else if (state == TDTouchIDStateTouchIDNotAvailable) { // TouchID 无效
                dispatch_async(dispatch_get_main_queue(), ^{
                    [btnSwitch setOn:NO];
                    [weak_self showMessage:[NSString stringWithFormat:@"%@无效",(type==TDTouchIDSupperTypeFaceID)?@"面容ID":@"触控ID"]];

                });
            }else if (state == TDTouchIDStatePasswordNotSet) { //  TouchID 无法启动,因为用户没有设置密码
                dispatch_async(dispatch_get_main_queue(), ^{
                    [btnSwitch setOn:NO];
                    [weak_self showMessage:[NSString stringWithFormat:@"%@无法启动,因为您没有设置密码",(type==TDTouchIDSupperTypeFaceID)?@"面容ID":@"触控ID"]];
                });
            }else if (state == TDTouchIDStateSystemCancel) { // TouchID 被系统取消 (如遇到来电,锁屏,按了Home键等)
                dispatch_async(dispatch_get_main_queue(), ^{
                [btnSwitch setOn:NO];
                });
            }else if (state == TDTouchIDStateInputPassword) { // 用户不使用TouchID,选择手动输入密码
                dispatch_async(dispatch_get_main_queue(), ^{
                [btnSwitch setOn:NO];
                });
            }else if (state == TDTouchIDStateUserCancel) { //TouchID 被用户手动取消
                dispatch_async(dispatch_get_main_queue(), ^{
                [btnSwitch setOn:NO];
                });
                NSLog(@"TouchID 被用户手动取消");
            }else if (state == TDTouchIDStateUserCancel) { //TDTouchIDStateFail
                dispatch_async(dispatch_get_main_queue(), ^{
                [btnSwitch setOn:NO];
                });
                NSLog(@"TouchID 验证失败");
            }else{//其他所有状态都是no
                dispatch_async(dispatch_get_main_queue(), ^{
                [btnSwitch setOn:NO];
                });
            }
            // ps:以上的状态处理并没有写完全!
            // 在使用中你需要根据回调的状态进行处理,需要处理什么就处理什么
            
        }];
    }else{//关闭指纹识别
//        [weak_self showMessage:@"关闭"];
      

    }
  
    
}

//生成秘钥对
//产生密钥
- (void)generateKeyAsync {
    CFErrorRef error = NULL;
        NSString *kTag = @"com.smartee.online3";

       SecAccessControlRef    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,
                                     (__bridge id)kSecAttrApplicationTag: kTag,//设置唯一表示 防止生成秘钥对失败
                                      (__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeECSECPrimeRandom,//表示产生ECC密钥对,注意目前只支持256位的ECC算法
                                       (__bridge id)kSecAttrAccessible:(__bridge id)kSecAttrAccessibleAlways,//设置经常 防止生成秘钥对失败
                                     (id)kSecAttrLabel: @"my-se-key",
                                     (id)kSecPrivateKeyAttrs: @{
                                             (id)kSecAttrAccessControl: (__bridge_transfer id)sacObject,
                                             (id)kSecAttrIsPermanent: @YES,
                                             }
                                     };
       //根据参数生成私钥
       NSError *gen_error = nil;
    SecKeyRef privateKey = (__bridge SecKeyRef)(CFBridgingRelease(SecKeyCreateRandomKey((__bridge CFDictionaryRef)parameters, (void *)&gen_error)));
//       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" };
//       SecKeyRef privateKey1;
//       OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)params, (CFTypeRef *)&privateKey1);
       //签名
//       NSError *error1;
       //提取公钥,进行验签,验签选择的算法必须与签名时的算法一致。
//       id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey1));
    SecKeyRef  publicKey = (__bridge SecKeyRef)(CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey)));
       //这里先把公钥保存到keychain才能拿到真正的公钥数据
       NSDictionary *pubDict = @{
                                 (__bridge id)kSecClass              : (__bridge id)kSecClassKey,
                                 (__bridge id)kSecAttrKeyType        : (__bridge id)kSecAttrKeyTypeECSECPrimeRandom,
                                 (__bridge id)kSecAttrLabel           : @"my-se-key",
                                 (__bridge id)kSecAttrIsPermanent    : @(YES),
                                 (__bridge id)kSecAttrKeySizeInBits: @256,
                                 (__bridge id)kSecAttrApplicationTag: kTag,//设置唯一表示 防止生成秘钥对失败
                                 (__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeECSECPrimeRandom,//表示产生ECC密钥对,注意目前只支持256位的ECC算法
                                  (__bridge id)kSecAttrAccessible:(__bridge id)kSecAttrAccessibleAlways,//设置经常 防止生成秘钥对失败
                                 (__bridge id)kSecValueRef           : (__bridge id)publicKey,
                                 (__bridge id)kSecAttrKeyClass       : (__bridge id)kSecAttrKeyClassPublic,
                                 (__bridge id)kSecReturnData         : @(YES)
                                 };
       CFTypeRef dataRef = NULL;
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)pubDict, &dataRef);
       if(status == errSecSuccess){
           NSLog(@"导出公钥成功");
           NSData *publicKeyData = (__bridge NSData *)dataRef;
           NSMutableData *data = [NSMutableData dataWithBytes:Secp256r1header
                                                       length:sizeof(Secp256r1header)];
           [data appendData:publicKeyData];
           NSString *strUUID = [self uuidString];
   //        NSString *strUUID =@"4fc669fc-ff96-48e2-873e-8f5ebe816103";


           NSLog(@"strUUID ----%@",strUUID);
//           NSString *publicStr = [publicKeyBits base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];

           NSString *publicStr = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
           //去除空格 换行
           NSString *publicStrKey = [self removeSpaceAndNewline:publicStr];
           NSString *strToken = DSStringValue([UserDefaults objectForKey:@"Token"]);
           NSString *strData = [NSString stringWithFormat:@"%@%@%@",strUUID,strToken,publicStrKey];
           NSLog(@"strData ----%@",strData);
           NSError *error;
           NSData *dataToSign = [strData dataUsingEncoding:NSUTF8StringEncoding];
           NSData *signature = CFBridgingRelease(SecKeyCreateSignature(privateKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)dataToSign, (void *)&error));
           NSString *sign =  [signature base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
           NSString *signData = [self removeSpaceAndNewline:sign];
           NSLog(@"sign ----%@",sign);
           //设备签名
           NSError *errorPrivate;
           NSString *strUUIDNew = [self removeSpaceAndNewline:strUUID];
           NSData *dataToSignPrivate = [strUUIDNew dataUsingEncoding:NSUTF8StringEncoding];
           NSData *signaturePrivate = CFBridgingRelease(SecKeyCreateSignature(privateKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)dataToSignPrivate, (void *)&errorPrivate));
           NSString *signUUID =  [signaturePrivate base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
           signUUID = [self removeSpaceAndNewline:signUUID];
           NSLog(@"sign1   -----------%@",signUUID);
           self.signUUid = signUUID;
           self.strUUid = DSStringValue(strUUID);
           NSLog(@"sign1 ----%@",signUUID);
           //提取公钥,进行验签,验签选择的算法必须与签名时的算法一致。
   //                id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey));
   //                Boolean verified = SecKeyVerifySignature((SecKeyRef)publicKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)dataToSign, (CFDataRef)signature, (void *)&error); if (verified == 1) {
   //                   NSString * message = [NSString stringWithFormat:@"signature:%@ verified:%@ error:%@", signature, @"验签成功", error];
   //                    NSLog(@"%@",message);
   //                }else{
   //                       NSString * message = [NSString stringWithFormat:@"signature:%@ verified:%@ error:%@", signature, @"验签失败", error];
   //                       NSLog(@"%@",message);
   //                }
           NSLog(@"sign1sign1sign1   -----------%@",signData );
           //提取公钥,进行验签,验签选择的算法必须与签名时的算法一致。
                   id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey));
                   Boolean verified = SecKeyVerifySignature((SecKeyRef)publicKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)dataToSign, (CFDataRef)signature, (void *)&error); if (verified == 1) {
                      NSString * message = [NSString stringWithFormat:@"signature:%@ verified:%@ error:%@", signature, @"验签成功", error];
                       NSLog(@"%@",message);
                   }else{
                          NSString * message = [NSString stringWithFormat:@"signature:%@ verified:%@ error:%@", signature, @"验签失败", error];
                          NSLog(@"%@",message);
                   }
           dispatch_async(dispatch_get_main_queue(), ^{
   //
               [self grantFingerRequestWithArr:@[DSStringValue(strUUID),DSStringValue(strToken),DSStringValue(publicStrKey),DSStringValue(signData)]];;
   //
           });

       }else{
           NSLog(@"导出公钥失败");
       }

}
//删除秘钥对
//删除密钥
- (void)deleteKeyAsync {

    NSDictionary *query = @{

                            (__bridge id)kSecAttrTokenID: (__bridge id)kSecAttrTokenIDSecureEnclave,

                            (__bridge id)kSecClass: (__bridge id)kSecClassKey,

                            (__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPrivate,

                            (__bridge id)kSecAttrLabel: @"my-seec-key",

                            (__bridge id)kSecReturnRef: @YES,

                            };

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

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

        if(status == errSecSuccess){

            NSLog(@"delete success");

        }else{

            NSLog(@"delete fail");

        }
    });

}

登录(若本地存有UUID 那么说明设置过 即可显示指纹登录/人脸登录按钮,并同时启动指纹/人脸登录;未存,则不显示、不启动)

- (void)verifyTouchIDInfo{
    DefinitionWeak(self);
    TDTouchIDSupperType type = [[TDTouchID sharedInstance] td_canSupperBiometrics];
    NSString * strTitle = (type==TDTouchIDSupperTypeFaceID?@"您尚未设置面容ID,请到设置中心设置您的面容ID":@"您尚未设置触控ID,请到设置中心设置您的触控ID");
    [[TDTouchID sharedInstance] td_showTouchIDWithDescribe:@"通过Home键验证已有指纹" FaceIDDescribe:@"通过已有面容ID验证" BlockState:^(TDTouchIDState state, NSError *error) {
        if (state == TDTouchIDStateNotSupport) {    //不支持TouchID/FaceID
            dispatch_async(dispatch_get_main_queue(), ^{
                [weak_self showMessage:@"当前设备不支持TouchID/FaceID"];
            });
        }else if (state == TDTouchIDStateSuccess) {    //TouchID/FaceID验证成功
            dispatch_async(dispatch_get_main_queue(), ^{
            [weak_self requestFingerLoginInfo];
            });
        }else if (state == TDTouchIDStateInputPasswordSuccess) {//密码输入成功
            NSLog(@"jump");
            dispatch_async(dispatch_get_main_queue(), ^{
                [weak_self requestFingerLoginInfo];
            });
        }else if (state == TDTouchIDStateInputPasswordFail) {//密码输入取消 或者错误
            dispatch_async(dispatch_get_main_queue(), ^{
                [self showMessage:@"请稍后重试"];
            });
        }else if (state == TDTouchIDStateTouchIDNotSet) { // TouchID 无法启动,因为用户没有设置TouchID
            NSLog(@"jump");
            [weak_self showAlertWithTitle:@"提示" message:strTitle cancelTitle:@"取消" okTitle:@"确定" completion:^(BOOL cancelled) {
                if (cancelled) {
                    NSLog(@"确定");
                    //跳转到设置中心
//                    NSURL * url = [NSURL URLWithString: UIApplicationOpenSettingsURLString];
                    NSURL *url = [NSURL URLWithString:@"App-Prefs:root=TOUCHID_PASSCODE"];
                     if ( [[UIApplication sharedApplication] canOpenURL: url] ) {
                         NSURL*url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
                       if (@available(iOS 10.0, *)){
                           [[UIApplication sharedApplication]openURL:url options:@{} completionHandler:^(BOOL success) {
                           }];
                         }else{
                             [[UIApplication sharedApplication] openURL:url];
                         }
                     }
                };
            }];
        }else if (state == TDTouchIDStateInvalidContext) { //当前软件被挂起并取消了授权 (LAContext对象无效)")
        }else if (state == TDTouchIDStateAppCancel) { // 当前软件被挂起并取消了授权 (如App进入了后台等)
            NSLog(@"jump");
        }else if (state == TDTouchIDStateTouchIDLockout) { // TouchID 被锁定(连续多次验证TouchID失败,系统需要用户手动输入密码)
            NSString *strMessage =[NSString stringWithFormat:@"%@被锁定,请稍后重试",(type==TDTouchIDSupperTypeFaceID)?@"面容ID":@"触控ID"];
            [weak_self showAlertWithTitle:@"提示" message:strMessage cancelTitle:@"取消" okTitle:@"确定" completion:^(BOOL cancelled) {
                if (cancelled) {
                    NSLog(@"确定");
                };
            }];
        }else if (state == TDTouchIDStateTouchIDNotAvailable) { // TouchID 无效
            dispatch_async(dispatch_get_main_queue(), ^{
                [weak_self showMessage:[NSString stringWithFormat:@"%@无效",(type==TDTouchIDSupperTypeFaceID)?@"面容ID":@"触控ID"]];
            });
        }else if (state == TDTouchIDStatePasswordNotSet) { //  TouchID 无法启动,因为用户没有设置密码
            dispatch_async(dispatch_get_main_queue(), ^{
                [weak_self showMessage:[NSString stringWithFormat:@"%@无法启动,因为您没有设置密码",(type==TDTouchIDSupperTypeFaceID)?@"面容ID":@"触控ID"]];
            });
        }else if (state == TDTouchIDStateSystemCancel) { // TouchID 被系统取消 (如遇到来电,锁屏,按了Home键等)
        }else if (state == TDTouchIDStateInputPassword) { // 用户不使用TouchID,选择手动输入密码
        }else if (state == TDTouchIDStateUserCancel) { //TouchID 被用户手动取消
            NSLog(@"TouchID 被用户手动取消");
        }else if (state == TDTouchIDStateFail) { //TDTouchIDStateFail
            dispatch_async(dispatch_get_main_queue(), ^{
                [weak_self showMessage:[NSString stringWithFormat:@"%@验证失败",(type==TDTouchIDSupperTypeFaceID)?@"面容ID":@"触控ID"]];
            });
        }else{//其他所有状态都是no
        }
        // ps:以上的状态处理并没有写完全!
        // 在使用中你需要根据回调的状态进行处理,需要处理什么就处理什么
        
    }];

}

参考文章:
1.ECDSA相关
2.验证签名及公钥是否有效

猜你喜欢

转载自blog.csdn.net/weixin_42050662/article/details/125222130