Android 指纹登录、支付

最近在做指纹登录,然后网上一大堆,but,几乎都只是说指纹识别,识别后如何登录几乎没有资料。

其他基础的就不说了,网上一大堆。。。


为什么Cipher需要包装传递给authenticate()方法?

Cipher传递给指纹验证方法,再取出来做加密解密,和直接用Cipher加密解密有什么区别呢?问题的关键还是在创建的Key上,创建keyGenerator时,有一个方法setUserAuthenticationRequired(true),也就是说这个key秘钥必须用户验证了才可以使用的,所以使用这种key初始化的的Cipher如果直接用于加密或者解密,会报出错误W/System.err: javax.crypto.IllegalBlockSizeException,仔细查看错误栈信息会发现它是由于android.security.KeyStoreException: Key user not authenticated这个错误引起的。而当我们将Cipher包装传递给指纹验证方法时,其内部验证了用户的身份,也就解除了Cipher中的key的使用限制,因此在回调方法中就可以使用该Cipher来加解密了。

如何保存相关信息以配合指纹验证身份?

指纹验证只是一个认证用户身份的方式,由于用于加密解密的实际操作其实是委托给CryptoObject内部的Cipher,因此主要的工作还是在Cipher的处理上,以指纹登录为例,加密方式是AES-CBC的对称加密:

首先是是加密,当用户选择开通指纹验证登录时,首先会要求用户输入登录密码,接下来代码中初始化一个Cipher用于加密操作,接着要求用户验证指纹,指纹验证成功后将登录密码通过Cipher加密并将做Base64转换成字符串,同时将Cipher的初始向量byte[] iv一起转换成字符串,将这两个字符串存储到服务器。同时,用于初始化Cipher的key保存在Android内部秘钥库AndroidKeyStore中,外部应用程序无法获取。

当用户发起登录时,首先从服务器获取之前加密过的密码字符串和初始向量字符串,同时从本地秘钥库AndroidKeyStore中通过alias别名取出之前存储的key,用初始向量和key初始化一个Cipher,接下来发起指纹登录,指纹验证通过后便可用这个Cipher解密字符串获取真正的登录密码,然后传递给服务器验证。

指纹支付同理...

开通指纹登录

if (mManager.isBiometricPromptEnable()) {
            mManager.authenticate(false, new BiometricPromptManager.OnBiometricIdentifyCallback() {
                @Override
                public void onUsePassword() {
                }

                @RequiresApi(api = Build.VERSION_CODES.M)
                @Override
                public void onSucceeded(FingerprintManager.AuthenticationResult result) {
                    try {
                        /**
                         * 加密后的密码和iv可保存在服务器,登录时通过接口根据账号获取
                         */
                        Log.i("test", "原密码: " + pwd);
                        Cipher cipher = result.getCryptoObject().getCipher();
                        byte[] bytes = cipher.doFinal(pwd.getBytes());
                        Log.i("test", "设置指纹时保存的加密密码: " + Base64.encodeToString(bytes,Base64.URL_SAFE));
                        aCache.put("pwdEncode", Base64.encodeToString(bytes,Base64.URL_SAFE));
                        byte[] iv = cipher.getIV();
                        Log.i("test", "设置指纹时保存的加密IV: " + Base64.encodeToString(iv,Base64.URL_SAFE));
                        aCache.put("iv", Base64.encodeToString(iv,Base64.URL_SAFE));
                        fingerLoginEnable = true;
                        Toast.makeText(MainActivity.this, "开通成功", Toast.LENGTH_LONG).show();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }

                @RequiresApi(api = Build.VERSION_CODES.P)
                @Override
                public void onSucceeded(BiometricPrompt.AuthenticationResult result) {
                    try {
                        Cipher cipher = result.getCryptoObject().getCipher();
                        byte[] bytes = cipher.doFinal(pwd.getBytes());
                        //保存加密过后的字符串
                        Log.i("test", "设置指纹保存的加密密码: " + Base64.encodeToString(bytes,Base64.URL_SAFE));
                        aCache.put("pwdEncode", Base64.encodeToString(bytes,Base64.URL_SAFE));
                        byte[] iv = cipher.getIV();
                        Log.i("test", "设置指纹保存的加密IV: " + Base64.encodeToString(iv,Base64.URL_SAFE));
                        aCache.put("iv", Base64.encodeToString(iv,Base64.URL_SAFE));
                        fingerLoginEnable = true;
                        Toast.makeText(MainActivity.this, "开通成功", Toast.LENGTH_LONG).show();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }

                @Override
                public void onFailed() {
                }

                @Override
                public void onError(int code, String reason) {
                }

                @Override
                public void onCancel() {
                }
            });
        }

 

使用指纹登录

if (mManager.isBiometricPromptEnable()) {
            mManager.authenticate(true, new BiometricPromptManager.OnBiometricIdentifyCallback() {
                @Override
                public void onUsePassword() {
                }

                @RequiresApi(api = Build.VERSION_CODES.M)
                @Override
                public void onSucceeded(FingerprintManager.AuthenticationResult result) {
                    try {
                        Cipher cipher = result.getCryptoObject().getCipher();
                        String text = aCache.getAsString("pwdEncode");
                        Log.i("test", "获取保存的加密密码: " + text);
                        byte[] input = Base64.decode(text, Base64.URL_SAFE);
                        byte[] bytes = cipher.doFinal(input);
                        /**
                         * 然后这里用原密码(当然是加密过的)调登录接口
                         */
                        Log.i("test", "解密得出的加密的登录密码: " + new String(bytes));
                        byte[] iv = cipher.getIV();
                        Log.i("test", "IV: " + Base64.encodeToString(iv,Base64.URL_SAFE));
                        Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_LONG).show();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @RequiresApi(api = Build.VERSION_CODES.P)
                @Override
                public void onSucceeded(BiometricPrompt.AuthenticationResult result) {
                    try {
                        Cipher cipher = result.getCryptoObject().getCipher();
                        String text = aCache.getAsString("pwdEncode");
                        Log.i("test", "获取保存的加密密码: " + text);
                        byte[] input = Base64.decode(text, Base64.URL_SAFE);
                        byte[] bytes = cipher.doFinal(input);
                        /**
                         * 然后这里用原密码(当然是加密过的)调登录接口
                         */
                        Log.i("test", "解密得出的原始密码: " + new String(bytes));
                        byte[] iv = cipher.getIV();
                        Log.i("test", "IV: " + Base64.encodeToString(iv,Base64.URL_SAFE));
                        Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_LONG).show();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onFailed() {
                }

                @Override
                public void onError(int code, String reason) {
                }

                @Override
                public void onCancel() {
                }
            });
        }

 

写文档还是蛮累的,我们代码里见吧(稍后放出...)

let`s gayhug

 https://github.com/Tendy-Lau/biometricdemo

 

你可能感兴趣的:(Android)