指纹识别的支持是Android6.0以后才开始的,Google也为指纹识别提供了一些列接口,指纹识别将要用到的核心API为FingerprintManager,其中还有三个核心内部类:FingerprintManager.AuthenticationResult 指纹识别后结果的回调,FingerprintManager.AuthenticationCallback指纹识别成功失败回调, FingerprintManager.CryptoObject指纹识别加密对象。其中最难实现的为CryptoObject的创建。为了加强指纹识别的安全级别我们还可以对需要传送的密码进行加密,通过KeyStore非对称加密实现。
方法名 | 参数 | 描述 |
---|---|---|
authenticate() | CryptoObject,CancellationSignal,flags,AuthenticationCallback,Handler | 用于开启指纹识别 |
isHardwareDetected() | 无 | 判断指纹识别硬件是否存在且能正常使用 |
hasEnrolledFingerprints() | 无 | 确定是否至少注册了一个指纹 |
如果只是简单测试可以只传入flags 和 AuthenticationCallback 即可
/**
* 获取FingerprintManager
*
* @return FingerprintManager
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public FingerprintManager getFingerprintManagerOrNull() {
if (getApplication().getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
return getApplication().getSystemService(FingerprintManager.class);
} else {
return null;
}
}
/**
* 指纹识别回调监听
*/
private FingerprintManager.AuthenticationCallback callback = new FingerprintManager.AuthenticationCallback() {
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
mTouchIdDialog.dismiss();
//指纹验证成功
Toast.makeText(MainActivity.this, "指纹验证成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
mTouchIdDialog.dismiss();
//指纹验证失败,不可再验
Toast.makeText(MainActivity.this, "onAuthenticationError:" + errString, Toast.LENGTH_SHORT).show();
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
mTouchIdDialog.startIconShackAnimation();
//指纹验证失败,可再验,可能手指过脏,或者移动过快等原因。
Toast.makeText(MainActivity.this, "onAuthenticationHelp:" + helpString, Toast.LENGTH_SHORT).show();
}
@Override
public void onAuthenticationFailed() {
mTouchIdDialog.startIconShackAnimation();
//指纹验证失败,指纹识别失败,可再验,该指纹不是系统录入的指纹。
Toast.makeText(MainActivity.this, "无法识别", Toast.LENGTH_SHORT).show();
}
};
if (mFingerprintManager.isHardwareDetected() && mFingerprintManager.hasEnrolledFingerprints()) {
mTouchIdStartBtn.setClickable(false);
if (mTouchIdDialog == null) {
mTouchIdDialog = new TouchIdDialog(MainActivity.this, R.style.TouchIdDialog);
mTouchIdDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
//如果dialog消失则取消指纹识别
if (mCancellationSignal != null && isStartAuthenticate) {
isStartAuthenticate = false;
mCancellationSignal.cancel();
mCancellationSignal = null;
}
mTouchIdStartBtn.setClickable(true);
}
});
}
mTouchIdDialog.show();
mCancellationSignal = new CancellationSignal();
if(mCryptoObjectCreator==null){
initCryptoObject();
}else {
//开始验证指纹
mFingerprintManager.authenticate(null,mCancellationSignal, 0, callback, null);
isStartAuthenticate = true;
}
}
一个简单的指纹识别demo我们就做好了,但是你们会看到我们并没用创建FingerprintManager.CryptoObject对象来进行加密,所以这样的识别是不安全的,在某种情况下会被其他具有威胁的应用破解,所以我们必须创建一个简单的指纹识别demo我们就做好了,但是你们会看到我们并没用创建FingerprintManager.CryptoObject对象来进行加密。
我们将通过Cipher和KeyStore来创建加密密钥,如果应用A创建了一个key,然后应用B通过AndroidKeyStore去获取该key,是获取不到的,这样我们也就实现了指纹识别的加密。我们也可以通过keyStore来对密码进行加密。通过keyStore加密后的数据可以随意存储于任意位置即使是sharedperference中,因为即使获取到数据也获取不到密钥去对数据进行解密。
private void initKeyStore(String alias){
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
}
catch(Exception e) {
e.printStackTrace();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
createNewKeys(alias);
}
}
private void createNewKeys(String alias){
if(!"".equals(alias)){
try {
// Create new key if needed
if (!keyStore.containsAlias(alias)) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 1);
KeyPairGeneratorSpec spec = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
spec = new KeyPairGeneratorSpec.Builder(IApplication.getApplication())
.setAlias(alias)
.setSubject(new X500Principal("CN=Sample Name, O=Android Authority"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
}
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
generator.initialize(spec);
}
KeyPair keyPair = generator.generateKeyPair();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public Cipher getCipher(String alias){
KeyStore.PrivateKeyEntry privateKeyEntry = null;
try {
privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
return output;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
这样FingerprintManager.CryptoObject对象我们就创建完成了,其使用方法如下
mFingerprintManager.authenticate(new FingerprintManager.CryptoObject(EncryUtils.getInstance().getCipher(mAlias))
, mCancellationSignal, 0, callback, null);
/**
* 加密方法
* @param needEncryptWord 需要加密的字符串
* @param alias 加密秘钥
* @return
*/
public String encryptString(String needEncryptWord, String alias) {
if(!"".equals(alias)&&!"".equals(needEncryptWord)){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
initKeyStore(alias);
}
String encryptStr="";
byte [] vals=null;
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
if(needEncryptWord.isEmpty()) {
return encryptStr;
}
Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
inCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, inCipher);
cipherOutputStream.write(needEncryptWord.getBytes("UTF-8"));
cipherOutputStream.close();
vals = outputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return Base64.encodeToString(vals, Base64.DEFAULT);
}
return "";
}
/**
* 解密方法
* @param needDecryptWord 需要解密的字符串
* @param alias key的别称
* @return
*/
public String decryptString(String needDecryptWord, String alias) {
if(!"".equals(alias)&&!"".equals(needDecryptWord)){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
initKeyStore(alias);
}
String decryptStr="";
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(needDecryptWord, Base64.DEFAULT)), output);
ArrayList values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte)nextByte);
}
byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}
decryptStr = new String(bytes, 0, bytes.length, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return decryptStr;
}
return "";
}
package com.daobao.asus.touchiddemo.keyStoreUtil;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.util.Base64;
import com.daobao.asus.touchiddemo.IApplication;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableEntryException;
import java.util.ArrayList;
import java.util.Calendar;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.security.auth.x500.X500Principal;
/**
* Created by xiongyu on 2016/12/1.
* 使用ksyStore加密工具类
*/
public class EncryUtils {
static EncryUtils encryUtilsInstance;
KeyStore keyStore;
public static EncryUtils getInstance() {
synchronized (EncryUtils.class) {
if (null == encryUtilsInstance) {
encryUtilsInstance = new EncryUtils();
}
}
return encryUtilsInstance;
}
private EncryUtils() {}
private void initKeyStore(String alias){
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
}
catch(Exception e) {
e.printStackTrace();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
createNewKeys(alias);
}
}
private void createNewKeys(String alias){
if(!"".equals(alias)){
try {
// Create new key if needed
if (!keyStore.containsAlias(alias)) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 1);
KeyPairGeneratorSpec spec = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
spec = new KeyPairGeneratorSpec.Builder(IApplication.getApplication())
.setAlias(alias)
.setSubject(new X500Principal("CN=Sample Name, O=Android Authority"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
}
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
generator.initialize(spec);
}
KeyPair keyPair = generator.generateKeyPair();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 加密方法
* @param needEncryptWord 需要加密的字符串
* @param alias 加密秘钥
* @return
*/
public String encryptString(String needEncryptWord, String alias) {
if(!"".equals(alias)&&!"".equals(needEncryptWord)){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
initKeyStore(alias);
}
String encryptStr="";
byte [] vals=null;
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
if(needEncryptWord.isEmpty()) {
return encryptStr;
}
Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
inCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, inCipher);
cipherOutputStream.write(needEncryptWord.getBytes("UTF-8"));
cipherOutputStream.close();
vals = outputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return Base64.encodeToString(vals, Base64.DEFAULT);
}
return "";
}
/**
* 解密方法
* @param needDecryptWord 需要解密的字符串
* @param alias key的别称
* @return
*/
public String decryptString(String needDecryptWord, String alias) {
if(!"".equals(alias)&&!"".equals(needDecryptWord)){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
initKeyStore(alias);
}
String decryptStr="";
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(needDecryptWord, Base64.DEFAULT)), output);
ArrayList values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte)nextByte);
}
byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}
decryptStr = new String(bytes, 0, bytes.length, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return decryptStr;
}
return "";
}
/**
* 获取私钥
* @param alias
* @return
*/
public PrivateKey getprivateKey(String alias){
initKeyStore(alias);
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
return privateKeyEntry.getPrivateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
return null;
}
public Cipher getCipher(String alias){
KeyStore.PrivateKeyEntry privateKeyEntry = null;
try {
privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
return output;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
}
以上代码并不完整如果需要完整代码可以到TouchID实例这个地址进行下载。