20

When I enrol first and only fingerprint and generate KeyPair the PrivateKey gets invalidated when I use it for the second time. This happens only once. Am I the only one having this issue? Is there something wrong with my code?

I cannot use any other key as I'm using PrivateKey to sign data.

Steps:

  1. Wipe all fingerprints
  2. Enrol one fingerprint
  3. Generate KeyPair and use FingerprintManager :: authenticate
  4. During next use of FingerprintManager :: authenticate PrivateKey gets permanently invalidated. This happens only for the first time

Below the code where I generate the 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();

And here is the code where I invoke fingerprint authentication for data signing:

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);
Toochka
  • 894
  • 1
  • 9
  • 25
  • Seems to me like you're attempting to reuse a key that was set up before you wiped the fingerprint data. Make sure you re setup your key after you wipe the fingerprints. You could try setting setInvalidatedByBiometricEnrollment to false and see what happens, – JohanShogun Nov 19 '16 at 10:46
  • At first I do wipe fingerprints, then enroll only one, generate keys. When I use generated keys for first time, everything works as expected, but when I authenticate with fingerpeint to use the key for second time it gets invalidated. I did try to use setInvalidateByBiometricEnrollment to false and it helped but this is not secure. – Toochka Nov 19 '16 at 12:20
  • Then it seems to me like this is an issue with the phonentoure using, a bug in the fingerprints software that manufacturer used, do you get the same result on all phones across manufacturers? – JohanShogun Nov 20 '16 at 07:20
  • Also I would have expected you to use a secret key, not a private key. :) – JohanShogun Nov 20 '16 at 07:22
  • Finally, if my memory doesn't betray me, in order to enroll or change finger prints the user has to authenticate themselves. So yes, allowing biometric enrollment is less secure, but not insecure. If the alternative is to regenerate your key when you get the exception (as it seems) then i would prefer allowing enrollment, as there are other reasons for the key to get invalidated, such as the user changing from a secure keyguard. If the normal screen lock is secure enough for your use case (check with the people who did the security analysis), this may be an alternative for you. – JohanShogun Nov 20 '16 at 07:25
  • In order to sign data I have to use ``PrivateKey`` hence cannot use ``SecretKey``. – Toochka Nov 25 '16 at 07:10
  • Looks like that this issue affects only Nexus devices. – Toochka Jan 04 '17 at 15:13

2 Answers2

2

i try this link and work perfectly .

First you need to set Minimum sdk look like the Picture

Image

Second set Permission in Mainfest

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

Third

generateKey() function which generates an encryption key which is then stored securely on the device.

cipherInit() function that initializes the cipher that will be used to create the encrypted FingerprintManager.

CryptoObject instance and various other checks before initiating the authentication process which is implemented inside onCreate() method.

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

Hope it help.

Community
  • 1
  • 1
0

you can see this one on github: hope it will help you : Confirm Credentials

Nawrez
  • 3,314
  • 8
  • 28
  • 42