Android指纹API与私钥/公钥

20
当我第一次注册并且只生成了一个指纹和KeyPair时,当我第二次使用PrivateKey时,它就会失效。这只会发生一次。我是唯一遇到这个问题的人吗?我的代码有什么问题吗?
我不能使用任何其他密钥,因为我正在使用 PrivateKey 来签署数据。
步骤:
  1. 清除所有指纹
  2. 注册一个指纹
  3. 生成KeyPair并使用FingerprintManager :: authenticate
  4. 在下一次使用FingerprintManager :: authenticate PrivateKey时,PrivateKey将永久失效。 这只会发生一次
以下是我生成KeyPair的代码:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(null);
KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
generator.initialize(new KeyGenParameterSpec.Builder("key_name", KeyProperties.PURPOSE_SIGN)
    .setDigests(digest) // I have defined digest before
    .setSignaturePaddings(paddings) // I have defined paddings before
    .setUserAuthenticationRequired(true)
    .build());
generator.generateKeyPair();

这是我调用指纹认证来进行数据签名的代码:

KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
Signature signature = Signature.getInstance("signing_algorithm");
PrivateKey privateKey = (PrivateKey) keyStore.getKey("key_name", null);
signature.initSign(privateKey); // Here I get KeyPermanentlyInvalidatedException
CryptoObject crypto = new CryptoObject(signature);
FingerprintManager fingerprintManager = context.getSystemService(FingerprintManager.class);
CancellationSignal cancellationSignal = new CancellationSignal();
AuthenticationCallback authenticationCallback = new AuthenticationCallback() {
    ...
};
fingerprintManager.authenticate(crypto, cancelationSignal, 0, authenticationCallback, null);

看起来你正在尝试重用在清除指纹数据之前设置的密钥。确保在清除指纹后重新设置密钥。你可以尝试将setInvalidatedByBiometricEnrollment设置为false,看看会发生什么。 - JohanShogun
起初我会清除指纹,然后只注册一次并生成密钥。当我第一次使用生成的密钥时,一切都按预期工作,但当我用指纹进行身份验证第二次使用该密钥时,它会失效。我尝试将setInvalidateByBiometricEnrollment设置为false,并且有所帮助,但这不安全。 - Toochka
那么在我看来,这似乎是您所使用的手机的问题,可能是制造商所使用的指纹识别软件中的一个bug。您在所有不同制造商的手机上都有相同的结果吗? - JohanShogun
我也希望你使用一个秘密密钥,而不是私钥。 :) - JohanShogun
似乎这个问题只影响Nexus设备。 - Toochka
显示剩余2条评论
2个回答

2
我试过这个链接,它完美地工作了。
首先,您需要将最小SDK设置为像图片一样。

Image

在清单文件中设置第二组权限。
   <uses-permission android:name="android.permission.USE_FINGERPRINT" />

第三个

generateKey() 函数生成一个加密密钥,然后将其安全地存储在设备上。

cipherInit() 函数初始化用于创建加密 FingerprintManager 的密码器。

CryptoObject 实例和各种其他检查,在启动实现在 onCreate() 方法中的身份验证过程之前进行。

FingerPrintActivty.java

import android.Manifest;
import android.annotation.TargetApi;
import android.app.KeyguardManager;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
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 FingerprintActivity extends AppCompatActivity {
 
 
   private KeyStore keyStore;
   // Variable used for storing the key in the Android Keystore container
   private static final String KEY_NAME = "androidHive";
   private Cipher cipher;
   private TextView textView;
 
 
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_fingerprint);
 
 
       // Initializing both Android Keyguard Manager and Fingerprint Manager
       KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
       FingerprintManager fingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
 
 
       textView = (TextView) findViewById(R.id.errorText);
 
 
       // Check whether the device has a Fingerprint sensor.
       if(!fingerprintManager.isHardwareDetected()){
           /**
            * An error message will be displayed if the device does not contain the fingerprint hardware.
            * However if you plan to implement a default authentication method,
            * you can redirect the user to a default authentication activity from here.
            * Example:
            * Intent intent = new Intent(this, DefaultAuthenticationActivity.class);
            * startActivity(intent);
            */
           textView.setText("Your Device does not have a Fingerprint Sensor");
       }else {
           // Checks whether fingerprint permission is set on manifest
           if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
               textView.setText("Fingerprint authentication permission not enabled");
           }else{
               // Check whether at least one fingerprint is registered
               if (!fingerprintManager.hasEnrolledFingerprints()) {
                   textView.setText("Register at least one fingerprint in Settings");
               }else{
                   // Checks whether lock screen security is enabled or not
                   if (!keyguardManager.isKeyguardSecure()) {
                       textView.setText("Lock screen security not enabled in Settings");
                   }else{
                       generateKey();
 
 
                       if (cipherInit()) {
                           FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
                           FingerprintHandler helper = new FingerprintHandler(this);
                           helper.startAuth(fingerprintManager, cryptoObject);
                       }
                   }
               }
           }
       }
   }
 
 
   @TargetApi(Build.VERSION_CODES.M)
   protected void generateKey() {
       try {
           keyStore = KeyStore.getInstance("AndroidKeyStore");
       } catch (Exception e) {
           e.printStackTrace();
       }
 
 
       KeyGenerator keyGenerator;
       try {
           keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
       } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
           throw new RuntimeException("Failed to get KeyGenerator instance", e);
       }
 
 
       try {
           keyStore.load(null);
           keyGenerator.init(new
                   KeyGenParameterSpec.Builder(KEY_NAME,
                   KeyProperties.PURPOSE_ENCRYPT |
                           KeyProperties.PURPOSE_DECRYPT)
                   .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                   .setUserAuthenticationRequired(true)
                   .setEncryptionPaddings(
                           KeyProperties.ENCRYPTION_PADDING_PKCS7)
                   .build());
           keyGenerator.generateKey();
       } catch (NoSuchAlgorithmException |
               InvalidAlgorithmParameterException
               | CertificateException | IOException e) {
           throw new RuntimeException(e);
       }
   }
 
 
   @TargetApi(Build.VERSION_CODES.M)
   public boolean cipherInit() {
       try {
           cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
       } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
           throw new RuntimeException("Failed to get Cipher", e);
       }
 
 
       try {
           keyStore.load(null);
           SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
                   null);
           cipher.init(Cipher.ENCRYPT_MODE, key);
           return true;
       } catch (KeyPermanentlyInvalidatedException e) {
           return false;
       } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException | NoSuchAlgorithmException | InvalidKeyException e) {
           throw new RuntimeException("Failed to init Cipher", e);
       }
   }
}

FingerprintAuthenticationHandler.Class

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.widget.TextView;
 
 
/**
* Created by whit3hawks on 11/16/16.
*/
public class FingerprintHandler extends FingerprintManager.AuthenticationCallback {
 
 
   private Context context;
 
 
   // Constructor
   public FingerprintHandler(Context mContext) {
       context = mContext;
   }
 
 
   public void startAuth(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) {
       CancellationSignal cancellationSignal = new CancellationSignal();
       if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
           return;
       }
       manager.authenticate(cryptoObject, cancellationSignal, 0, this, null);
   }
 
 
   @Override
   public void onAuthenticationError(int errMsgId, CharSequence errString) {
       this.update("Fingerprint Authentication error\n" + errString, false);
   }
 
 
   @Override
   public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
       this.update("Fingerprint Authentication help\n" + helpString, false);
   }
 
 
   @Override
   public void onAuthenticationFailed() {
       this.update("Fingerprint Authentication failed.", false);
   }
 
 
   @Override
   public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
       this.update("Fingerprint Authentication succeeded.", true);
   }
 
 
   public void update(String e, Boolean success){
       TextView textView = (TextView) ((Activity)context).findViewById(R.id.errorText);
       textView.setText(e);
       if(success){
           textView.setTextColor(ContextCompat.getColor(context,R.color.colorPrimaryDark));
       }
   }
}

希望它有所帮助。

0

你可以在 GitHub 上看到这个项目:希望它能对你有所帮助:确认凭据


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接