iOS fingerprint recognition/face recognition login (ECDSA signature)

Basic logic of biometric identification:
1. You need to add the Privacy-Face ID Usage Description attribute in the plist file
In order to facilitate your quick login, XXXXX needs to access your face ID/fingerprint ID

2. Obtain authorization
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. Call the required VC

Setting up fingerprint unlocking/face recognition unlocking for the first time:
(1) After successfully obtaining authorization, determine which identification method the system supports.
(2) After the verification is successful, the randomly generated UUID + token + public key string is ECDSA signed; at the same time, the public key string, token, and UUID are passed to the background to make the The device is bound to the user; after the binding is successful, the UUID and signature can be saved
(3) When logging in, the UUID and signed UUID are passed to the backend, and the backend uses the public key to verify the signature. , you can log in to your account after successful signature verification.

first time setup

#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");

        }
    });

}

Log in (if the UUID is stored locally, it means that it has been set up to display the fingerprint login/face login button and activate the fingerprint/face login button at the same time; if it is not saved, it will not be displayed or started)

- (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:以上的状态处理并没有写完全!
        // 在使用中你需要根据回调的状态进行处理,需要处理什么就处理什么
        
    }];

}

Reference articles:
1.ECDSA related
2. Verify whether the signature and public key are valid

Guess you like

Origin blog.csdn.net/weixin_42050662/article/details/125222130