Android Fingerprint API и закрытые/открытые ключи

Когда я регистрирую первый и единственный отпечаток пальца и генерирую KeyPair, PrivateKey становится недействительным, когда я использую его во второй раз. Это происходит только один раз. У меня одного такая проблема? Что-то не так с моим кодом?

Я не могу использовать никакой другой ключ, так как я использую PrivateKey для подписи данных.

Шаги:

  1. Стереть все отпечатки пальцев
  2. Зарегистрировать один отпечаток пальца
  3. Создайте KeyPair и используйте FingerprintManager :: authenticate
  4. При следующем использовании FingerprintManager :: authenticate 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);

person Toochka    schedule 25.10.2016    source источник
comment
Мне кажется, вы пытаетесь повторно использовать ключ, который был настроен до того, как вы стерли данные отпечатка пальца. Убедитесь, что вы перенастроили свой ключ после того, как стерли отпечатки пальцев. Вы можете попробовать установить для setInvalidatedByBiometricEnrollment значение false и посмотреть, что произойдет,   -  person JohanShogun    schedule 19.11.2016
comment
Сначала делаю вайпы отпечатков пальцев, потом прописываю только один, генерирую ключи. Когда я использую сгенерированные ключи в первый раз, все работает так, как ожидалось, но когда я аутентифицируюсь с помощью fingerpeint, чтобы использовать ключ во второй раз, он становится недействительным. Я пытался использовать setInvalidateByBiometricEnrollment для false, и это помогло, но это небезопасно.   -  person Toochka    schedule 19.11.2016
comment
Тогда мне кажется, что это проблема с использованием phonentoure, ошибка в программном обеспечении для отпечатков пальцев, которое использовал производитель, вы получаете одинаковый результат на всех телефонах разных производителей?   -  person JohanShogun    schedule 20.11.2016
comment
Также я ожидал, что вы будете использовать секретный ключ, а не закрытый ключ. :)   -  person JohanShogun    schedule 20.11.2016
comment
Наконец, если мне не изменяет память, чтобы зарегистрировать или изменить отпечатки пальцев, пользователь должен пройти аутентификацию. Так что да, разрешение биометрической регистрации менее безопасно, но не небезопасно. Если альтернативой является повторное создание вашего ключа, когда вы получаете исключение (как кажется), тогда я бы предпочел разрешить регистрацию, так как есть другие причины, по которым ключ становится недействительным, например, пользователь переходит с безопасного ключа. Если обычная блокировка экрана достаточно безопасна для вашего случая использования (проконсультируйтесь с людьми, которые проводили анализ безопасности), это может быть альтернативой для вас.   -  person JohanShogun    schedule 20.11.2016
comment
Чтобы подписать данные, я должен использовать PrivateKey, следовательно, не могу использовать SecretKey.   -  person Toochka    schedule 25.11.2016
comment
Похоже, эта проблема затрагивает только устройства Nexus.   -  person Toochka    schedule 04.01.2017


Ответы (2)


я пробую эту ссылку и отлично работаю.

Сначала вам нужно установить минимальный размер SDK, как на картинке.

Изображение

Второй набор разрешений в Mainfest

   <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));
       }
   }
}

Надеюсь, это поможет.

person Mohammad Hossein jfp    schedule 11.04.2017

вы можете увидеть это на github: надеюсь, это поможет вам: Подтвердить учетные данные

person Nawrez    schedule 15.10.2017