I’m implementing a “use fingerprint instead of password” feature for devices with nexus imprint.
It’s fairly easy to prompt for a fingerprint and see if was correct or not but I’m scratching my head trying to protect a value with fingerprint, I’m following this example https://github.com/googlesamples/android-FingerprintDialog but there is no “recover value with fingerprint”, it only explains how to store it
any good example of something like that?
The API expects a password so the general idea is:
user activates "protect with fingerprint"
is asked for the password
if correct, is asked for fingerprint
if correct, password is securely stored
for any subsequent logins, when touching the fingerprint scanner, if correct, the value is decrypted and sent to server for login
Okay, found the way, just for the record, here are the steps:
Init Cypher for decryption:
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
Create the CryptoObject with Cypher:
CryptoObject cryptoObject = new FingerprintManagerCompat.CryptoObject(cipher);
fingerprintManager.authenticate(cryptoObject, 0, cancellationSignal, callback, null);
Check the onAuthenticationSucceeded(AuthenticationResult) and get the value:
Cipher cipher = authenticationResult.getCryptoObject().getCipher();
byte[] encryptedBytes = cipher.doFinal("1234".getBytes("UTF-8"));
Related
Few days ago, In "Pre-launch report for APK" in Google Play Console, it start to flag me
Unsafe encryption
Detected in APK ???
Your app contains unsafe cryptographic encryption patterns. Please see this Google Help Centre article for details.
Vulnerable classes:
c.j.a.s.J.b
However, since the early day of APK, I do not change anything in encryption code/ description code. Hence, I'm not sure why Google starts to warn me on recent APK?
Any idea how to resolve? As, the information for vulnerable classes c.j.a.s.J.b is not helpful.
I try to use Proguard + mapping.txt to retrace c.j.a.s.J.b but able to figure what class is that.
Any idea how I can get rid of Google security warning?
The google play suggests with vulnerable classes with the function name, you can see in the dialog.
Review your app for statically computed keys, initialization vectors, and/or salts that are used in cryptographic encryption operations and ensure that these values are constructed safely
For example :
public byte[] encryptionUtil(String key, String iv, byte[] plainText) {
Cipher cipher = Cipher.getInstance(“AES/GCM/NoPadding”);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), “AES”);
GCMParameterSpec paramSpec = new GCMParameterSpec(256, iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, paramSpec);
return cipher.doFinal(plainText);
}
And you are calling a function as:
byte[] cipherText = encryptionUtil(“abcdef...”, “010203040506”, plainText);
Here your encryption key “abcdef...” is provides as a static string. A statically computed value is a value that is the same on every execution of your app. Statically computed cryptographic values can be extracted from your app and used to attack your app’s encrypted data.
So you can use EncryptedSharedPreferences to store locally data
Reference link https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences
OR
Jetpack Security
For more details:
Remediation for Unsafe Cryptographic Encryption
I think you are using some encryption/decryption code with statically stored key.
A statically computed value is a value that is the same on every execution of your app. Statically computed cryptographic values can be extracted from your app and used to attack your app’s encrypted data.
So Google give this warning to change that stored key with dynamically generated key.
For that you can generate different key on every launch.
To solve this problem generate dynamic encryption/decryption key on every launch.
For that you can find more info here https://developer.android.com/jetpack/androidx/releases/security
I have a question to ask you guys as below.
Using FingerprintManager passing crytoObject type is Cipher.
The cipher I have init with a privateKey in decryption mode.
The privateKey is generated from AndroidKeystore with setUserAuthenticationRequired (true).
So, this private key will be used in cipher like cipher.init(DecryptionMode, privatekey), passing this cipher in Fingerprintmanager.authenticate.
After user verify I can successfully use the cryptoobject result, successfully get the cipher and do decryption (doFinal) in an encrypted String.
After done decrypt the string, I have to proceed signature because I need to use same private key second time for signature and get a complete string.
Here is the problem, it will always throw exception user not authenticated, is that mean cryptoObject result return from FingerprintManager after successfully verified the fingerprint its cipher type object can use for once only ? I can use it once for dofinal decrypt an encrypted string and after that I have to use the same private key for signature it will hit user not authenticated exception.
Does it mean user need to verify biometric again? It can only be used 1 time after user successfully verify, the cipher which I done init(decrypt mode , private key) it return and cannot use twice. I already tested setUserAuthenticationValidityDurationSeconds (int seconds) this will always hit not authenticated and I didnt use setUserAuthenticationValidityDurationSeconds .
Please I need expert help on fingerprintManager (cryptoObject result) when authenticate successful, the cipher type with privateKey can only use one time. I need to continue use it second time without hitting user not authenticated. Please.
setUserAuthenticationValidityDurationSeconds this is not working.
cipher init PrivateKey and pass into fingerprintManager.authenticate(cryptoObject)
When fingerprint successfully verified, the cipher can be used one time only. If need to use the same private key in signature it will hit user not authenticated. How to make the private key available ?
You can think of there being two "types" of keys: per-use keys, meaning authentication must take place for every use of the key, and time-based keys, which are available for an app-specified time after authentication.
Both types of keys are created with setUserAuthenticationRequired(true), but with different time parameters in setUserAuthenticationValidityDurationSeconds(t). Per-use keys (t == -1) can only be unlocked by biometrics, whereas time-based keys (t > 0) can be unlocked by biometrics or pin/pattern/password.
Biometric only
Create key using t = -1 (default)
Unlock using BiometricPrompt#authenticate(CryptoObject)
Password or biometric
Create key using t > 0
Unlock using lockscreen, or BiometricPrompt with setDeviceCredentialAllowed(true)
You can see the sample androidx.biometric app here, which implements both use cases.
I wish to use the Android hardware-backed KeyStore, but I'm concerned about security and usability.
From what I've read here, KeyStore gets wiped when the user changes the device lock, unless setEncryptionRequired() is omitted. For usability sake, it seems this needs to be done, otherwise all hardware-backed keys would get wiped once the device lock is modified.
However, I've also read here that hardware-backed keys are not actually stored in the TEE, but rather, stored as key files in /data/misc/keystore/user_0/, encrypted by a device specific key that is stored within the TEE. Since a change in device lock wipes the KeyStore, it seems that the device specific key is derived from the device lock.
For security reasons, it makes sense to encrypt the key file, otherwise any root user would be able to read the key files and extract the private key, since they'd presumably be in they clear.
So I'm kind of in a dilemma. For usability sake, I should omit setEncryptionRequired(), but for security sake, I should set setEncryptionRequired().
Lastly, is it possible import a private key into the hardware-backed KeyStore using setKeyEntry()? I'm able to do so with no errors but I'm not sure if it's hardware-backed.
Is my understanding correct?
setEncryptionRequired() was deprecated in Android 6.0 (Marshmallow), and never really accomplished very much. The security of Android KeyStore depends on the TEE, not the password.
The blog post you linked to is out of date, at least on devices running Android 6.0 or later. On those devices, you should not use setEncryptionRequired(), and your keys will not be deleted until your app is uninstalled (or a factory reset is done, or your app deletes them). Your keys will be securely wrapped by secret keys that never leave the TEE. In fact, your keys will never leave the TEE in plaintext. When you use your keys, the data is passed into the TEE along with the encrypted key. The TEE unwraps the key then processes and returns the encrypted/signed/whatever data.
Yes, you can import private keys using setKeyEntry(). If you want to be sure that your key is hardware-backed, use KeyInfo.isInsideSecureHardware(). For example (this is from the documentation):
PrivateKey key = ...; // Android KeyStore key
KeyFactory factory = KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
KeyInfo keyInfo;
boolean isHardwareBacked = false;
try {
keyInfo = factory.getKeySpec(key, KeyInfo.class);
isHardwareBacked = keyInfo.isInsideSecureHardware();
} catch (InvalidKeySpecException e) {
// Not an Android KeyStore key.
}
I'm making an app that will require a password to be entered before the main app will load. I plan to get the user to create a password when they first run the app and then store the password on the local device and encrypt it using a local symmetric key (which will be generated when the app first runs). This is so someone can't simply read the file where the password is stored.
How can I store the key used securely? Or is there a better way of hiding stored passwords to be used in local verification?
The app is designed for offline usage so I can't add any networking capabilities.
You can use SharedPreferences in private mode to store the password. It is secure as far as the phone is not rooted but you can use Cryptography techniques to store the password. The approach which I follow to store the passwords locally is to add a SALT to the password while storing.
You can read more about it here
A secure way for passwords - hashing. A hash can never be decrypted as the password is lost during the hashing process. I'm using MD5 hashing process in the following code -
public String StringToMD5(String s) {
try {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
for (int i=0; i<messageDigest.length; i++)
M hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
Use this code to hash your password and then store it using Private SharedPreferences. When the user enters the password again, hash it again and check if it is the same as previous hash. If the hashes match, then access is granted.
Please start by reading Thomas Pornin's canonical answer to How to securely hash passwords?.
PBKDF2 options are listed in the question PBKDF2 function in Android, but include a native SecretKeyFactory method as well as Spongycastle, bouncycastle, rtner.de, etc.
Long, cryptographically random per-password salt is required (make room for more than one password for future growth!).
Never ask PBKDF2 for more key length than the native function supports - that 64 bytes for PBKDF2-HMAC-SHA-512, and 20 bytes for PBKDF2-HMAC-SHA-1.
Always use as high an iteration count as your users can stand. Even for android devices, for a single user on their own device, done only once at application start, that's in the hundreds of thousands or more for PBKDF2-HMAC-SHA-1 and the tens to hundreds of thousands for PBKDF2-HMAC-SHA-512.
Note that PBKDF2's primary use is in creating encryption keys - you can use the same password entered to generate the encryption key for files you encrypt using AES (NOT in ECB mode); just use a different salt and a different number of iterations. If you're only doing that, then you don't even need the password hash; simply try decrypting the file with the key generated and a stored salt and number of iterations - if it works, it was the right password. If it fails, it wasn't.
no key distribution, public and private keys will be known by users (random key generator will not be used). I have to encrypt hashed message with private key in order to provide signature
message will only 10-20 characters, so system can be as simple as it is possible
For generating a digital signature, you don't need to encrypt the hash. Signing is a separate crypto primitive; the hash is not encrypted raw anyway, there's some padding. That said, the code is:
Signature Signer = Signature.getInstance("SHA1withRSA");
Signer.initSign(MyKey, new SecureRandom()); //Where do you get the key?
byte []Message = MyMessage(); //Initialize somehow
Signer.update(Message, 0, Message.length);
byte [] Signature = Sign.sign();
Okay, back up and tell us what you want. Are you trying to get privacy by protecting the contents of the message, or guarantee authenticity by showing that the message really came from the originator?
If you're looking for privacy, RSA isn't the way to go: use RSA to generate a private/public pair, and then use them to excahnge keys -- or exchange keys out of band. Use a streaming algorithm like AES to encrypt the message.
If you just want signature to show the message was originated by who you think it was, then have a look at the Wiki article on digital signature -- it's reasonably straightforward.