乐趣区

关于xamarin:MASA-MAUI-Plugin-五Android-指纹识别

背景

MAUI 的呈现,赋予了宽广 Net 开发者开发多平台利用的能力,MAUI 是 Xamarin.Forms 演变而来,然而相比 Xamarin 性能更好,可扩展性更强,构造更简略。然而 MAUI 对于平台相干的实现并不残缺。所以 MASA 团队发展了一个实验性我的项目,意在对微软 MAUI 的补充和扩大,

我的项目地址https://github.com/BlazorComp…

每个性能都有独自的 demo 演示我的项目,思考到 app 安装文件体积(尽管 MAUI 曾经集成裁剪性能,然而该性能对于代码自身有影响),届时每一个性能都会以独自的 nuget 包的模式提供,不便测试,当初我的项目才刚刚开始,然而置信很快就会有能够交付的内容啦。

前言

本系列文章面向挪动开发小白,从零开始进行平台相干性能开发,演示如何参考平台的官网文档应用 MAUI 技术来开发相应性能。

介绍

API 级别 23 (Android 6.0) 设施上引入指纹扫描仪为应用程序提供了传统的用户名 / 明码用户身份验证的代替办法。相较于用户名和明码,采纳指纹对用户进行身份验证使应用程序安全性的实现更具隐衷性,之后 API-28(Android9.0) 中增加了生物辨认身份验证Biometric,减少了人脸认证相干性能。咱们明天探讨的只波及指纹认证,思考到兼容性问题采纳API – 23 (Android 6.0) 版本提供的 FingerprintManager API,通过测试能够在 Android 6.0 -11.0 中失常工作,如果您须要人脸验证相干性能请参考链接: androidx.biometric,实现细节与本文相似。

思路

咱们先看一下 Android 的指纹验证办法外围的指纹治理类 FingerprintManagerCompat,fingerprintManager 是通过 FingerprintManagerCompat.from(Context context)来创立的。

JAVA 代码
FingerprintManagerCompat fingerprintManager= FingerprintManagerCompat.from(Context context);

1、查看资格:

1、须要查看设施是否反对指纹。
2、须要查看设施是否受爱护 – 用户必须应用屏幕锁爱护设施。如果用户未应用屏幕锁爱护设施,然而以后应用程序对于安全性要求很高,则应告诉用户必须配置屏幕锁。
3、须要检查用户是否曾经注册指纹 – 用户必须至多有一个指纹已注册到操作系统。此权限查看应在每次尝试进行身份验证之前进行,因为用户有可能随时勾销指纹
在 MAUI blazor 我的项目的 Platforms->Android 文件夹增加MasaMauiFingerprintService.cs 类,增加如下两个办法

       public static class MasaMauiFingerprintService
    {private static FingerprintManagerCompat fingerprintManager = FingerprintManagerCompat.From(Android.App.Application.Context);

        /// <summary>
        /// Check eligibility
        /// </summary>
        /// <returns>error message</returns>
        public static async Task<string> CheckingEligibility()
        {
            // 1、Check if your hardware supports it
            if (!fingerprintManager.IsHardwareDetected)
            {return "IsHardwareDetected";}

            // 2、Check if the user is using a screen lock
            // KeyguardManager: Lock screen management class
            var keyguardManager = Android.App.Application.Context.GetSystemService(Context.KeyguardService) as KeyguardManager;
            if (!keyguardManager.IsKeyguardSecure)
            {return "The device does not have a screen lock set";}

            // 3、Check if at least one fingerprint is registered
            if (!fingerprintManager.HasEnrolledFingerprints)
            {return "The device does not have a fingerprint set, please set at least one fingerprint";}

            var granted = await CheckAndRequestFingerprintPermission();
            if (!granted)
            {return "Permissions not granted";}
            return string.Empty;
        }

        /// <summary>
        /// Permission check
        /// </summary>
        /// <returns></returns>
        private static async Task<bool> CheckAndRequestFingerprintPermission()
        {var status = await Permissions.CheckStatusAsync<AndroidFingerprintPermissions>();

            if (status == PermissionStatus.Granted)
                return true;

            status = await Permissions.RequestAsync<AndroidFingerprintPermissions>();

            if (status == PermissionStatus.Granted)
                return true;
            return false;
        }

        /// <summary>
        /// Permissions required for fingerprints
        /// </summary>
        private class AndroidFingerprintPermissions : Permissions.BasePlatformPermission
        {public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
                new List<(string androidPermission, bool isRuntime)>
                {(global::Android.Manifest.Permission.UseFingerprint, true),
                }.ToArray();}
    }

CheckingEligibility 顺次查看设施是否反对蓝牙(IsHardwareDetected)、设施是否有屏幕锁(IsKeyguardSecure)这里须要一个 KeyguardManager 的类帮忙查看、是否注册了至多一个指纹(HasEnrolledFingerprints)、是否对立了应用指纹相干权限。

2、扫描指纹实现

当初,咱们应用 FingerprintManager 的 Authenticate 办法进行指纹验证,

JAVA 代码
public void authenticate (FingerprintManager.CryptoObject crypto, 
                CancellationSignal cancel, 
                int flags, 
                FingerprintManager.AuthenticationCallback callback, 
                Handler handler)

参数:
crypto FingerprintManager.CryptoObject:这是一个加密类的对象,指纹扫描器会应用这个对象来判断认证后果的合法性。这个对象能够是 null,然而这样的话,就象征这 app 无条件信赖认证后果,所以这个过程可能被攻打,数据能够被篡改。因而,倡议这个参数不要置为 null。
cancel CancellationSignal:这个对象用来在指纹识别器扫描用户指纹的是时候勾销以后的扫描操作,如果不勾销的话,那么指纹扫描器会移植扫描直到超时(个别为 30s,取决于具体的厂商实现),这样的话就会比拟耗电。倡议这个参数不要置为 null。辨认过程中能够手动勾销指纹识别
flags int:没用,传 0
callback FingerprintManager.AuthenticationCallback:要接管身份验证事件的回调办法,此值不能为 null
handler Handler:FingerprintManagerCompat 将会应用这个 handler 中的 looper 来解决来自指纹识别硬件的音讯。一般来说,咱们开发的时候能够间接传 null,因为 FingerprintManagerCompat 会默认应用 app 的 main looper 来解决

咱们持续在当前目录下增加 CryptoObjectHelper.cs

public class CryptoObjectHelper
    {
        // 键值名称,利用中须要放弃惟一
        static readonly string KEY_NAME = "com.masa-maui-blazor.android.sample.fingerprint_authentication_key";

        // 写死不必改
        static readonly string KEYSTORE_NAME = "AndroidKeyStore";

        // 加密算法参数 不必改
        static readonly string KEY_ALGORITHM = KeyProperties.KeyAlgorithmAes;
        static readonly string BLOCK_MODE = KeyProperties.BlockModeCbc;
        static readonly string ENCRYPTION_PADDING = KeyProperties.EncryptionPaddingPkcs7;
        static readonly string TRANSFORMATION = KEY_ALGORITHM + "/" +
                                                BLOCK_MODE + "/" +
                                                ENCRYPTION_PADDING;
        readonly KeyStore _keystore;

        public CryptoObjectHelper()
        {_keystore = KeyStore.GetInstance(KEYSTORE_NAME);
            _keystore.Load(null);
        }

        public FingerprintManagerCompat.CryptoObject BuildCryptoObject()
        {var cipher = CreateCipher();
            return new FingerprintManagerCompat.CryptoObject(cipher);
        }

        Cipher CreateCipher(bool retry = true)
        {var key = GetKey();
            var cipher = Cipher.GetInstance(TRANSFORMATION);
            try
            {cipher.Init(CipherMode.EncryptMode, key);
            }
            catch (KeyPermanentlyInvalidatedException e)
            {_keystore.DeleteEntry(KEY_NAME);
                if (retry)
                {CreateCipher(false);
                }
                else
                {throw new Exception("Could not create the cipher for fingerprint authentication.", e);
                }
            }
            return cipher;
        }

        IKey GetKey()
        {
            IKey secretKey;
            if (!_keystore.IsKeyEntry(KEY_NAME))
            {CreateKey();
            }

            secretKey = _keystore.GetKey(KEY_NAME, null);
            return secretKey;
        }

        void CreateKey()
        {var keyGen = KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, KEYSTORE_NAME);
            var keyGenSpec =
                new KeyGenParameterSpec.Builder(KEY_NAME, KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
                    .SetBlockModes(BLOCK_MODE)
                    .SetEncryptionPaddings(ENCRYPTION_PADDING)
                    .SetUserAuthenticationRequired(true)
                    .Build();
            keyGen.Init(keyGenSpec);
            keyGen.GenerateKey();}
    }

CryptoObjectHelper 类应用 Android KeyGenerator 生成密钥并平安地将其存储在设施上。
创立的键类型的元数据由类的 KeyGenParameterSpec 实例提供。
应用 GetInstance 工厂办法实例化 AKeyGenerator
上述代码应用 (AES) 作为加密算法。KeyGenParameterSpec.Builde包装具体的 AES 加密配置信息,
SetUserAuthenticationRequired(true) 示意在应用密钥之前须要用户身份验证。

咱们在 MasaMauiFingerprintService 增加一个验证的办法,其中自定义的MasaMauiAuthCallback,在上面一节介绍。

        public static void FingerPrintAuthentication()
        {fingerprintManager.Authenticate(new CryptoObjectHelper().BuildCryptoObject(), 0, new CancellationSignal(), new MasaMauiAuthCallback(), null);
        }

3、响应身份验证回调

咱们在当前目录持续增加 CallBack 类 MasaMauiAuthCallback.cs,该类须要继承 FingerprintManagerCompat.AuthenticationCallback,至多须要重写OnAuthenticationSucceeded 办法

public class MasaMauiAuthCallback : FingerprintManagerCompat.AuthenticationCallback
    {
        // 轻易写,然而 app 内放弃惟一
        byte[] SECRET_BYTES = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
        
        public override void OnAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result)
        {if (result.CryptoObject.Cipher != null) // 应用了 Cipher
            {var doFinalResult = result.CryptoObject.Cipher.DoFinal(SECRET_BYTES);

                if (doFinalResult.Any())
                {MessagingCenter.Send<MasaMauiAuthCallback,string>(this, "Validation", "验证胜利");

                }
            }
            else
            {
                // 没有应用 Cipher?
                // 咱们这里的示例应用了 Cipher,临时不思考不实用的状况
            }
        }

        public override void OnAuthenticationFailed()
        {
            // 告诉用户验证失败
            MessagingCenter.Send<MasaMauiAuthCallback, string>(this, "Validation", "验证失败");
        }
    }

除了 OnAuthenticationSucceededOnAuthenticationFailed之外,还有 onAuthenticationHelponAuthenticationError
咱们这里暂不思考其余两种情景,有趣味能够参考链接: AuthenticationCallback
这里为了不便演示,验证胜利或者失败都是通过 MessagingCenter 音讯发送进来,在应用的时候须要订阅对应 topic,来获取验证后果。

4、测试

新建一个 MAUI Blazor 我的项目 Masa.Blazor.Maui.Plugin.BiometricsSample
AndroidManifest.xml文件中增加指纹须要的权限 USE_FINGERPRINT

<uses-permission android:name="android.permission.USE_FINGERPRINT" />

简略批改一下 Index.razor 便于测试
Index.razor:

@page "/"

Welcome to your new app.

<MButton Block OnClick="Fingerprint"> 验证指纹 </MButton>

Index.razor.cs:

using Masa.Blazor.Maui.Plugin.Biometrics;
using Microsoft.AspNetCore.Components;

namespace Masa.Blazor.Maui.Plugin.BiometricsSample.Pages
{
    public partial class Index
    {[Inject]
        private IPopupService PopupService {get; set;}
        private async Task Fingerprint()
        {var checkingEligibilityErrorMessage = await MasaMauiFingerprintService.CheckingEligibility();
            if (string.IsNullOrEmpty(checkingEligibilityErrorMessage))
            {await HandledValidationAsync();
                MasaMauiFingerprintService.FingerPrintAuthentication();}
            else
            {await PopupService.ToastErrorAsync(checkingEligibilityErrorMessage);
            }
        }

        private async Task HandledValidationAsync()
        {
            // Cancel your subscription first to prevent duplicate subscriptions
            MessagingCenter.Unsubscribe<MasaMauiAuthCallback, string>(this, "Validation");
            MessagingCenter.Subscribe<MasaMauiAuthCallback, string>(this, "Validation", (sender, arg) =>
            {PopupService.ToastInfoAsync(arg);
            });
        }
    }
}

这里我应用到 MAUI 提供的公布和订阅音讯 MessagingCenter,参考连贯链接: MessagingCenter
代码比较简单,先查看资格,没有报错信息之后开启指纹验证,并异步接管 callback 办法公布的音讯。
启动一下,别离用正确和谬误的指纹进行测试:
不同的手机指纹验证的 UI 不同,我这里是 vivo 的手机


如果你对咱们的开源我的项目感兴趣,无论是代码奉献、应用、提 Issue,欢送分割咱们

  • WeChat:MasaStackTechOps
  • QQ:7424099
退出移动版