Android指纹锁

时间:2020-02-23 14:28:55  来源:igfitidea点击:

在本教程中,我们将讨论Android指纹API,并在我们的android应用程序中实现"指纹对话框"。

Android指纹管理器

指纹管理器是用于从设备(如果存在)访问指纹硬件的类。

Google建议通过向用户显示带有指纹图标的DialogFragment来对应用程序中的指纹进行身份验证。

为了实现指纹认证,您需要在AndroidManifest.xml文件中添加以下权限:

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

以下是在您的应用程序中实现指纹认证的步骤:

  • 检查锁定屏幕上是否有安全锁定

  • 使用FingerprintManager类检查指纹硬件是否可用。

  • 检查用户是否注册了至少一个指纹。

  • 获取对Android密钥库的访问权限,以存储用于启动密码的密钥。

  • 启动身份验证方法并添加回调方法

Android Keystore系统可让您将加密密钥存储在容器中,以使其更难从设备中提取。

代码

下面给出了" activity_main.xml"布局的代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">

  <Button
      android:id="@+id/button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="START AUTHENTICATION"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="parent" 

</android.support.constraint.ConstraintLayout>

下面给出了" dialog_fingerprint.xml"的代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:paddingLeft="24dp"
  android:paddingTop="24dp"
  android:paddingRight="24dp"
  app:layout_constraintLeft_toLeftOf="parent"
  app:layout_constraintRight_toRightOf="parent">

  <TextView
      android:id="@+id/titleTextView"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:text="Fingerprint Dialog"
      android:textAppearance="?android:attr/textAppearanceLarge"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent" 

  <TextView
      android:id="@+id/subtitleTextView"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginTop="20dp"
      android:text="Confirm fingerprint to continue."
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toBottomOf="@id/titleTextView" 

  <android.support.design.widget.FloatingActionButton
      android:id="@+id/fab"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="28dp"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintTop_toBottomOf="@id/subtitleTextView"
      app:srcCompat="@drawable/ic_fingerprint_white_24dp" 

  <TextView
      android:id="@+id/errorTextView"
      android:layout_width="0dp"
      android:layout_height="0dp"
      android:layout_marginStart="16dp"
      android:gravity="center_vertical"
      android:text="Touch sensor"
      app:layout_constraintBottom_toBottomOf="@id/fab"
      app:layout_constraintLeft_toRightOf="@id/fab"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="@id/fab" 

  <LinearLayout
      android:id="@+id/buttons"
      style="?android:attr/buttonBarStyle"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginTop="16dp"
      android:gravity="end"
      android:orientation="horizontal"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toBottomOf="@id/fab">

      <Button
          android:id="@+id/btnCancel"
          style="?android:attr/buttonBarButtonStyle"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="Cancel" 
  </LinearLayout>

</android.support.constraint.ConstraintLayout>

在FingerprintHelper.java类中,我们定义了指纹和相关类对象的身份验证和初始化方法:

package com.theitroad.androidfingerprintapi;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
import android.support.v4.app.ActivityCompat;

public class FingerprintHelper extends FingerprintManager.AuthenticationCallback {

  private Context mContext;
  private FingerprintManager mFingerprintManager;
  private CancellationSignal mCancellationSignal;
  private Callback mCallback;

  public FingerprintHelper(FingerprintManager fingerprintManager, Context context, Callback callback) {
      mContext = context;
      mFingerprintManager = fingerprintManager;
      mCallback = callback;

  }

  public boolean isFingerprintAuthAvailable() {

      return mFingerprintManager.isHardwareDetected()
              && mFingerprintManager.hasEnrolledFingerprints();
  }

  public void startAuthentication(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) {

      if (!isFingerprintAuthAvailable())
          return;

      mCancellationSignal = new CancellationSignal();
      if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
          return;
      }

      manager.authenticate(cryptoObject, mCancellationSignal, 0, this, null);
  }

  public void stopListening() {
      if (mCancellationSignal != null) {
          mCancellationSignal.cancel();
          mCancellationSignal = null;
      }
  }

  @Override
  public void onAuthenticationError(int errMsgId, CharSequence errString) {
      mCallback.onError(errString.toString());
  }

  @Override
  public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
      mCallback.onHelp(helpString.toString());
  }

  @Override
  public void onAuthenticationFailed() {
      mCallback.onAuthenticated(false);
  }

  @Override
  public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
      mCallback.onAuthenticated(true);
  }

  public interface Callback {

      void onAuthenticated(boolean b);

      void onError(String s);

      void onHelp(String s);
  }
}

manager.authenticate(cryptoObject,mCancellationSignal,0,this,null);开始认证。

回调接口用于将信息传递到UI,最终将其显示给用户。

MainActivity.java类的代码如下:

package com.theitroad.androidfingerprintapi;

import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

  Button button;
  FingerprintManagerCompat managerCompat;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      button = findViewById(R.id.button);
      button.setOnClickListener(this);

  }

  private void showFingerPrintDialog() {

      FingerprintDialog fragment = new FingerprintDialog();
      fragment.setContext(this);
      fragment.show(getSupportFragmentManager(), "");

  }

  @Override
  public void onClick(View v) {
      switch (v.getId()) {
          case R.id.button:
              managerCompat = FingerprintManagerCompat.from(MainActivity.this);

              if (managerCompat.isHardwareDetected() && managerCompat.hasEnrolledFingerprints()) {
                  showFingerPrintDialog();
              } else {
                  Toast.makeText(getApplicationContext(), "Fingerprint 不支持", Toast.LENGTH_SHORT).show();
              }
              break;
      }
  }

}

managerCompat = FingerprintManagerCompat.from(MainActivity.this);初始化FingerprintCompat类对象。

isHardwareDetected()和hasEnrolledFingerprints()用于在显示对话框之前检查是否可以进行指纹认证。

FingerprintDialog.java的代码如下:

package com.theitroad.androidfingerprintapi;

import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.support.v4.app.DialogFragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

public class FingerprintDialog extends DialogFragment
      implements FingerprintHelper.Callback {

  Button mCancelButton;
  public static final String DEFAULT_KEY_NAME = "default_key";
  FingerprintManager mFingerprintManager;

  private FingerprintManager.CryptoObject mCryptoObject;
  private FingerprintHelper mFingerprintHelper;

  KeyStore mKeyStore = null;
  KeyGenerator mKeyGenerator = null;
  KeyguardManager mKeyguardManager;

  private Context mContext;

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setRetainInstance(true);
      setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog);

      try {
          mKeyStore = KeyStore.getInstance("AndroidKeyStore");

      } catch (KeyStoreException e) {
          e.printStackTrace();
      }

      try {
          mKeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
      } catch (NoSuchAlgorithmException e) {
          e.printStackTrace();
      } catch (NoSuchProviderException e) {
          e.printStackTrace();
      }

      Cipher defaultCipher;
      try {
          defaultCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
                  + KeyProperties.BLOCK_MODE_CBC + "/"
                  + KeyProperties.ENCRYPTION_PADDING_PKCS7);
      } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
          throw new RuntimeException("Failed to get an instance of Cipher", e);
      }

      mKeyguardManager = getContext().getSystemService(KeyguardManager.class);
      mFingerprintManager = getContext().getSystemService(FingerprintManager.class);

      mFingerprintHelper = new FingerprintHelper(mFingerprintManager, getContext(), this);

      if (!mKeyguardManager.isKeyguardSecure()) {
          Toast.makeText(getContext(),
                  "Lock screen not set up.\n"
                          + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint",
                  Toast.LENGTH_LONG).show();
          return;
      }

      createKey(DEFAULT_KEY_NAME);

      if (initCipher(defaultCipher, DEFAULT_KEY_NAME)) {
          mCryptoObject = new FingerprintManager.CryptoObject(defaultCipher);
      }
  }

  private boolean initCipher(Cipher cipher, String keyName) {
      try {
          mKeyStore.load(null);
          SecretKey key = (SecretKey) mKeyStore.getKey(keyName, null);
          cipher.init(Cipher.ENCRYPT_MODE, key);

          return true;
      } catch (KeyPermanentlyInvalidatedException e) {
          Toast.makeText(mContext, "Keys are invalidated after created. Retry the purchase\n"
                          + e.getMessage(),
                  Toast.LENGTH_LONG).show();

          return false;
      } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
              | NoSuchAlgorithmException | InvalidKeyException e) {
          Toast.makeText(mContext, "Failed to init cipher", Toast.LENGTH_LONG).show();
          return false;
      }
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                           Bundle savedInstanceState) {
      View v = inflater.inflate(R.layout.dialog_fingerprint, container, false);
      mCancelButton = v.findViewById(R.id.btnCancel);
      mCancelButton.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              dismiss();
          }
      });

      return v;
  }

  @Override
  public void onResume() {
      super.onResume();

      if (mCryptoObject != null) {
          mFingerprintHelper.startAuthentication(mFingerprintManager, mCryptoObject);
      }
  }

  @Override
  public void onPause() {
      super.onPause();
      mFingerprintHelper.stopListening();
  }

  public void setContext(Context context) {
      mContext = context;
  }

  public void createKey(String keyName) {
      try {
          mKeyStore.load(null);
         

          KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyName,
                  KeyProperties.PURPOSE_ENCRYPT |
                          KeyProperties.PURPOSE_DECRYPT)
                  .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                  .setUserAuthenticationRequired(true)
                  .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);

          mKeyGenerator.init(builder.build());
          mKeyGenerator.generateKey();
      } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
              | CertificateException | IOException e) {
          throw new RuntimeException(e);
      }
  }

  @Override
  public void onAuthenticated(boolean b) {
      if (b) {
          Toast.makeText(mContext.getApplicationContext(), "Auth success", Toast.LENGTH_LONG).show();
          dismiss();
      } else
          Toast.makeText(mContext.getApplicationContext(), "Auth failed", Toast.LENGTH_LONG).show();
  }

  @Override
  public void onError(String s) {
      Toast.makeText(mContext.getApplicationContext(), s, Toast.LENGTH_LONG).show();
  }

  @Override
  public void onHelp(String s) {
      Toast.makeText(mContext.getApplicationContext(), "Auth help message:" + s, Toast.LENGTH_LONG).show();
  }
}

setRetainInstance(true);用于防止配置更改时创建DialogFragment的多个实例。

当我使用正确的指纹进行身份验证时,手机上会显示相应的Toast消息。