check if the user changed biometric/fingerprint in Android - android

I am looking for a way to get notify if the user changed his/her fingerprint.
I saw this answer here but it wasn't clear how to use "setAllowedAuthenticators" in this scenario.
I would appreciate it if someone could help.
[update] the updated code:
1- Generate secret key
generateSecretKey(new KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
// Invalidate the keys if the user has registered a new biometric
// credential, such as a new fingerprint. Can call this method only
// on Android 7.0 (API level 24) or higher. The variable
.setInvalidatedByBiometricEnrollment(true)
.build());
2- Generate cipher
Cipher cipher = getCipher();
SecretKey secretKey = getSecretKey();
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
} catch (KeyPermanentlyInvalidatedException e) {
System.out.print("key has changed");
} catch (InvalidKeyException e) {
e.printStackTrace();
}
3- authenticate
biometricPrompt.authenticate(new CancellationSignal(), excutor, new BiometricPrompt.AuthenticationCallback() {
#Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
}
});
}
});
}
});
Error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.fingerprint_poc, PID: 9523
java.lang.IllegalArgumentException: keystoreAlias must not be empty
at android.security.keystore.KeyGenParameterSpec$Builder.<init>(KeyGenParameterSpec.java:760)
at com.example.fingerprint_poc.task$5.onClick(task.java:153)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25885)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) I/Process: Sending signal. PID: 9523 SIG: 9

Using the functions here link and link
You add this code before the authentication
Cipher cipher = getCipher();
SecretKey secretKey = getSecretKey();
if (getSecretKey() == null){
generateSecretKey(new KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
// Invalidate the keys if the user has registered a new biometric
// credential, such as a new fingerprint. Can call this method only
// on Android 7.0 (API level 24) or higher. The variable
.setInvalidatedByBiometricEnrollment(true)
.build());
}
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
} catch (KeyPermanentlyInvalidatedException e) {
System.out.print("key has changed");
Toast.makeText(task.this, "changed", Toast.LENGTH_LONG).show();
generateSecretKey(new KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
// Invalidate the keys if the user has registered a new biometric
// credential, such as a new fingerprint. Can call this method only
// on Android 7.0 (API level 24) or higher. The variable
.setInvalidatedByBiometricEnrollment(true)
.build());
} catch (InvalidKeyException e) {
e.printStackTrace();
}
Important: the KEY_NAME must be the same, it must not change between instances.

Related

Android keystore2.AndroidKeyStoreProvider throws NPE on Samsung devices with API 31

I'm getting lots of crash reported on samsung devices running android 12 from yesterday. AndroidKeyStoreProvider throws NPE when calling X509Certificate.getPublicKey(). it's working fine on other brands devices.
Key pair generation method :
public void generateKeys(Context context) throws GeneratePairKeyException {
KeyPairGenerator keyPairGenerator;
try {
if(KeyStoreUtil.getPrivateKey() == null){
Calendar start = GregorianCalendar.getInstance();
Calendar end = GregorianCalendar.getInstance();
end.add(Calendar.YEAR, 10);
keyPairGenerator = KeyPairGenerator.getInstance("RSA", ANDROID_KEY_STORE);
keyPairGenerator.initialize(new KeyPairGeneratorSpec.Builder(context)
.setAlias(ALIAS)
.setSerialNumber(BigInteger.valueOf(1))
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.setSubject(new X500Principal("CN=Pushe_CA"))
.build());
keyPairGenerator.generateKeyPair();
}
} catch (NoSuchAlgorithmException |
NoSuchProviderException | InvalidAlgorithmParameterException e) {
Logger.e(e);
throw new GeneratePairKeyException("pair key not created");
} catch (IllegalStateException e) {
throw e;
} catch (Exception e) {
throw new GeneratePairKeyException("pair key not created");
}
}
App crashes when i'm trying to get private key like this :
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
(PrivateKey) keyStore.getKey(ALIAS, null);
here's the crash log :
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.PublicKey java.security.cert.X509Certificate.getPublicKey()' on a null object reference
at android.security.keystore2.AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse(AndroidKeyStoreProvider.java:220)
at android.security.keystore2.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(AndroidKeyStoreProvider.java:401)
at android.security.keystore2.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(AndroidKeyStoreProvider.java:343)
at android.security.keystore2.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.java:110)
at java.security.KeyStore.getKey(KeyStore.java:1062)
Any solutions?

Implemented Finger Print Authentication in Application with signature

I am getting the issue when running the application below android 6.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
FingerprintManager fingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!fingerprintManager.isHardwareDetected()) {
/**
* An error message will be displayed if the device does not contain the fin.gerprint hardware.
* However if you plan to implement a default authentication method,
* you can redirect the user to a default authentication ctivity from here.
* Example:
*
*/
} else {
// Checks whether fingerprint permission is set on manifest
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
// permission not granted
} else {
// Check whether at least one fingerprint is registered
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Checks whether lock screen security is enabled or not
if (!keyguardManager.isKeyguardSecure()) {
// finger print not support
} else {
if (!fingerprintManager.hasEnrolledFingerprints()) {
// finger print not enrolled
} else {
/*Amit Verma EC signature*/
createKeyPair();
if (initSignature()) {
ConstantDeclaration.mCryptoObject = new FingerprintManager.CryptoObject(mSignature);
}
}
}
}
}
}
}
}
Here is code for key-pair Generation
#TargetApi(Build.VERSION_CODES.M)
private void createKeyPair() {
// The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
// for your flow. Use of keys is necessary if you need to know if the set of
// enrolled fingerprints has changed.
try {
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
KeyPairGenerator mKeyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
mKeyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_SHA1)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
// Require the user to authenticate with a fingerprint to authorize
// every use of the private key
// .setUserAuthenticationRequired(true)
.setUserAuthenticationRequired(false)
.build());
mKeyPairGenerator.generateKeyPair();
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
}
here is the code for signature initiate.
#TargetApi(Build.VERSION_CODES.M)
private boolean initSignature() {
try {
mKeyStore = KeyStore.getInstance("AndroidKeyStore");
mKeyStore.load(null);
PrivateKey key = (PrivateKey) mKeyStore.getKey(KEY_NAME, null);
// String strKey = Base64.encodeToString(key.getEncoded(),Base64.DEFAULT);
// System.out.println("PrivateKey::"+strKey);
mSignature = Signature.getInstance("SHA1withECDSA");
mSignature.initSign(key);
PublicKey publicKey = mKeyStore.getCertificate(KEY_NAME).getPublicKey();
String strPublicKey = Base64.encodeToString(publicKey.getEncoded(), Base64.DEFAULT);
Singleton.getInstance().public_key_fp = strPublicKey;
return true;
} catch (KeyPermanentlyInvalidatedException e) {
return false;
} catch (Exception e) {
throw new RuntimeException("Failed to init Cipher", e);
}/*catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
}*/
}
This code is working fine but when I am running my application in below Android 6.0 getting the exception on the launching the app.
Android version: 19
Device: samsung SM-J100ML
App version: 7
Line Number1: java.lang.Class.newInstanceImpl(Native Method)
Description : java.lang.VerifyError: launcherActivity
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1208)
at android.app.Instrumentation.newActivity(Instrumentation.java:1068)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2288)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2493)
at android.app.ActivityThread.access$800(ActivityThread.java:166)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1283)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5584)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
at dalvik.system.NativeStart.main(Native Method)
I think the problem with Signature import is import java.security.Signature;
I figured out the problem by reading logcat as said above in comment,
Could not find class 'android.security.keystore.KeyGenParameterSpec$Builder'
and
Could not find class 'android.hardware.fingerprint.FingerprintManager'
I googled above classes and found this link here
So I removed catch block InvalidAlgorithmParameterException ,**KeyPermanentlyInvalidatedException** and replaced with parent class "Exception" only.

Removal of Fingerprint not triggering InvalidKeyException for Fingerprnt Authentication

I am facing issue with Fingerprint authentication, actually, I have integrated Fingerprint Authentication in my app and working fine except one case.
I have set up two Fingerprint in my device, after initializing KeyGenerator with Fingerprint KEY, I have removed one Fingerprint from device and come back to my app and performing Fingerprint Auth it is working fine. I don't know why it is not triggering InvalidKeyException as like adding Fingerprint works. Is this expected behavior or any bug with OS?
Device Details are below,
Device : Pixel
OS: Android 8.0
Code of my implementation follows,
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);
}
}
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);
}
}
}
I have already tried with few posts(How to detect the removal fingerprint in Android?, Android Fingerprint API and Private/Public keys, Android Key Invalidation when Fingerprints removed), nothing helped me to get rid of it.
Your code is not strictly tied to fingerprints. All it has that is somewhat related is:
setUserAuthenticationRequired(true)
That says that the key that you are creating in the AndroidKeyStore requires user authentication. It does not say anything about fingerprints.
If the user has fingerprints registered, the user could use a fingerprint to authenticate for the purposes of using this key. That would be for any fingerprint registered by the user, and in your case, you still have a registered fingerprint (even after the user removed the second fingerprint). Also, the user does not have to use a fingerprint to authenticate — they could use their passphrase, PIN, or pattern, if they choose.
Is this expected behavior or any bug with OS?
This is expected behavior.

createConfirmDeviceCredentialIntent will unlock after pressing the back button numerous times in a row?

I recently implemented fingerprint authentication into one of my apps. Everything works as expected except for this one problem that I discovered when testing. Some background: I generated a key with the following key gen parameters:
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(80)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
so the user will remain authenticated for 80 seconds. Any time the app needs to re-verify the user, it will check if the key is still valid, and otherwise createConfirmDeviceCredentialIntent if the key timed out, which brings up a system screen prompting the user to authenticate their fingerprint or enter a password/pin/pattern.
The problem: Now this screen should be un-cancelable, however if I tap the back button or top arrow button multiple times in a row, the user gets into the app. Has anyone had this experience?
EDIT:
This seems to only be happening on Samsung devices I tested, it works well on the Nexus and Google Pixel.
Here is the code being used, it's from the Google sample project on using createConfirmDeviceCredentialIntent.
This is the method to check the if the key is still valid.
protected boolean tryEncrypt() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
mKeyStore = KeyStore.getInstance("AndroidKeyStore");
mKeyStore.load(null);
SecretKey secretKey = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
mCipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
mCipher.init(Cipher.ENCRYPT_MODE, secretKey);
mCipher.doFinal(SECRET_BYTE_ARRAY);
return true;
} catch (UserNotAuthenticatedException e) {
// User is not authenticated, let's authenticate with device credentials.
showAuthenticationScreen();
return false;
} catch (KeyPermanentlyInvalidatedException e) {
return false;
} catch (BadPaddingException | IllegalBlockSizeException | KeyStoreException |
CertificateException | UnrecoverableKeyException | IOException
| NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
return false;
}
Here is the showEncryption method that is called if the key is timed out:
protected void showAuthenticationScreen() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent intent = getKeyguardManager().createConfirmDeviceCredentialIntent(null, null);
if (intent != null) {
startActivityForResult(intent, 1);
}
}
}

How to Generate Key and Chiper initiation below Android API 23?

Am Using Fingerprint Authentication for App. Want to Support for below API 23 also. For that am Using FingerprintManagerCompat. I don't know how to Generate Key and Chiper initiation in Pre-Android API 23.
Below Code am Used for API 23 - Generate Key
protected void generateKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (Exception e) {
e.printStackTrace();
}
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);
}
}
Below Code am Used for API 23 - Chiper initiation
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 (KeyStoreException | CertificateException
| UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
}
}
I don't know how initiate these two things in Pre API 23 to access FingerprintManagerCompat, Help me to solve this issue.
FingerprintManager is not available in pre-marshmallow devices. They have added this API in Marshamallow which is specified here
No.You can't Generate Key and cipher below API 23(Marshmallow 6.0).
Some android device have fingerprint sensor below API 21 but android only supports for API 23 and above.You have to use their sdk for fingerprint authentication.Samsung provide their own sdk for fingerprint authentication i.e Pass Sdk.
You can see this link.Sample project for Finger Print Authentication here

Categories

Resources