最近在做指纹登录,然后网上一大堆,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