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.
}
Related
I am working on an app that uses Key Store Keys to enable authentication with the server. When the user logs in I am creating a key pair that requires Authentication to be used either by fingerprint or device credentials.
The problem I have is that I am trying for the keys to get invalidated every time the user changes the security in his device either by enrolling fingerprints or changing the PIN/PASSWORD/PATTERN.
I know that the Key specs have the setInvalidatedByBiometricEnrollment() method but from what I read it only invalidates the keys if there's a change in the biometrics, that doesn't help me if the user is only using device credentials. Furthermore, that method was added on API 24 and I am targetting devices starting with API 23.
This is the way I am creating the keys:
//Purposes of the key
int keyPurp =
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT |
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY;
//Init a builder for the key.
KeyGenParameterSpec.Builder keyBuilder = new KeyGenParameterSpec.Builder(keyAlias,keyPurp)
//We set the valid formats of the digests for signing
keyBuilder.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512);
//Require the user to authenticate and session expiry
keyBuilder.setUserAuthenticationRequired(true);
keyBuilder.setUserAuthenticationValidityDurationSeconds(10);
KeyGenParameterSpec keySpec = keyBuilder.build();
//Initialize the generator of the keys
generator.initialize(keySpec);
//Get the keys
generator.generateKeyPair();
Is there any Android API to detect changes in the Pin/Pattern/Password configuration?
Check out the docs for the isInvalidatedByBiometricEnrollment and setInvalidatedByBiometricEnrollment.
isInvalidatedByBiometricEnrollment
Returns true if the key is irreversibly invalidated when a new biometric is enrolled or all enrolled biometrics are removed. This has effect only for keys that require biometric user authentication for every use.
^ Important part is the last bit has effect only for keys that require biometric user authentication for every use.
setInvalidatedByBiometricEnrollment
Sets whether this key should be invalidated on biometric enrollment. This applies only to keys which require user authentication (see setUserAuthenticationRequired(boolean)) and if no positive validity duration has been set (see setUserAuthenticationValidityDurationSeconds(int), meaning the key is valid for biometric authentication only.
^ Important part there is if no positive validity duration has been set.
So if you want your keys to be invalidated by changes to biometry, they cannot have a duration attached to them. They must be single use.
We have noticed the keys actually cannot be used even if they have validity duration set to them, but you do not get correct Exception. And you cannot tell difference between a usage outside of duration period or if the key is invalidated.
For security reasons I want to store my private key in the HSM and then share the private key with another app via the KeyChain. The KeyStore mentions that it can be backed by the HSM but I haven't found any documentation that states it's possible with the KeyChain.
Based on the KeyChain documentation, the KeyChain is the proper way to share between apps:
Use the KeyChain API when you want system-wide credentials. When an
app requests the use of any credential through the KeyChain API, users
get to choose, through a system-provided UI, which of the installed
credentials an app can access. This allows several apps to use the
same set of credentials with user consent.
So, is it possible to secure the Android KeyChain with an HSM?
After a lot of searching, I finally found that the answer is yes, it is possible.
The Android 4.3 changelog seems to be the only place that documents it:
Android also now supports hardware-backed storage for your KeyChain
credentials, providing more security by making the keys unavailable
for extraction. That is, once keys are in a hardware-backed key store
(Secure Element, TPM, or TrustZone), they can be used for
cryptographic operations but the private key material cannot be
exported. Even the OS kernel cannot access this key material.
I tested this with code and it seems that once you import a KeyPair into the KeyChain it is automatically put into secure hardware. This is the Kotlin code that I ran to test:
GlobalScope.launch {
context?.let { it1 ->
val privKey = KeyChain.getPrivateKey(it1, "device_certificate")
Log.d("App", privKey.toString()) // Shows that this is an AndroidKeyStoreRSAPrivateKey
val keyFactory: KeyFactory = KeyFactory.getInstance(privKey?.algorithm, "AndroidKeyStore")
val keyInfo: KeyInfo = keyFactory.getKeySpec(privKey, KeyInfo::class.java)
if (keyInfo.isInsideSecureHardware()) {
Log.d("App", "The key is in secure hardware!")
}
else {
Log.d("App", "The key is not in secure hardware!")
}
}
}
which printed "The key is in secure hardware!".
I want to encrypt and decrypt with symmetrically with android Keystore KMS. I'm aware with Google cloud KMS, and AWS KMS, but I don't want to handle with that platform.
How to manage this generated Android Keystore private key for both (client, server) sides?
I have created a private key for encrypting and decrypting, but hard to manage for the store and share this key.
I had stored that private key in Private SharedPreferences for reuse but There is one problem is that, this private SharedPreferences is not secured because all can observe this private SharedPreferences file in the rooted device.
Refer this link to get information about generating a private key for Android Keystore.
I'm new with tink, so please help me to out this. if there is a wrong thing in my idea then feel free to give your opinion.
Android Keystore is a client side KMS, you cannot use it on server side.
If you want to use Tink with Android Keystore on Android, please take a look at AndroidKeysetManager. Here's an example:
String masterKeyUri = "android-keystore://my_master_key_id";
AndroidKeysetManager manager = AndroidKeysetManager.Builder()
.withSharedPref(getApplicationContext(), "my_keyset_name", "my_pref_file_name")
.withKeyTemplate(SignatureKeyTemplates.ECDSA_P256)
.withMasterKeyUri(masterKeyUri)
.build();
PublicKeySign signer = PublicKeySignFactory.getPrimitive(manager.getKeysetHandle());
This will read a keyset stored in the my_keyset_name preference of the my_pref_file_name preferences file. If the preference file name is null, it uses the default preferences file.
If the keyset is not found or invalid, and a valid KeyTemplate is set with AndroidKeysetManager.Builder.withKeyTemplate(com.google.crypto.tink.proto.KeyTemplate), a fresh keyset is generated and is written to the my_keyset_name preference of the my_pref_file_name shared preferences file.
On Android M or newer and if a master key URI is set with AndroidKeysetManager.Builder.withMasterKeyUri(java.lang.String), the keyset is encrypted with a master key generated and stored in Android Keystore. When Tink cannot decrypt the keyset it would assume that it is not encrypted.
The master key URI must start with android-keystore://. If the master key doesn't exist, a fresh one is generated. Usage of Android Keystore can be disabled with AndroidKeysetManager.Builder.doNotUseKeystore().
On Android L or older, or when the master key URI is not set, the keyset will be stored in cleartext in private preferences which, thanks to the security of the Android framework, no other apps can read or write.
I have this scenario where my App needs to make requests towards a secure server (NON http(s), actually it is about SIP protocol but the question should apply to any non http(s) protocol), and I need be able to tell if the server is considered trusted, based on the System Default Trusted certificates installed in my Android device's keystore.
The problem is that after checking all the APIs Android provides for certificates (like KeyStore, KeyChain, etc) I haven't been able to find a solution.
Seems that each app, even though it can gain access to the System Default keystore of the device, it can only access it's own resources, not global, even when we are talking about TrustedCertificateEntry-type entries.
Is there anything I'm missing here?
Seems like a pretty valid use case for non-https authentication
Best regards,
Antonis
Finally, managed to find a way to do this, so let me share in case this can be useful to others. Turns out Android gives access to system wide trusted certificates. The detail here (and the reason it didn't work for me previously) was the keystore 'type' identifier that I used:
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
Which I believe was trying to find actual keys, which off course shouldn't be shared. So after some digging I found that there's a separate type, AndroidCAStore, which did the trick for me. So here's a working code excerpt, that just prints out certificates:
try {
KeyStore ks = KeyStore.getInstance("AndroidCAStore");
ks.load(null);
try {
Enumeration<String> aliases = ks.aliases();
while (aliases.hasMoreElements()) {
Certificate cert = ks.getCertificate(aliases.nextElement());
Log.e(TAG, "Certificate: " + cert.toString());
}
}
catch(Exception e) {
e.printStackTrace();
}
}
catch (IOException|NoSuchAlgorithmException|KeyStoreException|CertificateException e) {
e.printStackTrace();
}
I'm developing an Py4a (Python in SL4a) application running on an Android phone.
The application collects some sensitive data, and then sends it via e-mail, using the smtplib module.
To assure necessary protection, I need to encrypt that information.
As phone is considered to be unsafe device, I have to use the public-key encryption, and only recipient's public key should be stored in the phone.
The standard Py4a distribution contains two packages supporting public key cryptography:
the ssl and gdata.
Unfortunately none of them provides a ready to use function, allowing me to encrypt longer information with the private key.
Well, I know that in fact I should generate a random temporary symmetric key, encrypt my information with that key, and finally encrypt only this key with the recipient's public key. However there are some details which must be considered to obtain secure solution...
So here my question comes. Is there any simple encryption library, well suited to Py4a (ie. based on the cryptographic libraries already available in Py4a - like ssl and gdata.Crypto) providing easy to use public key encryption?
Update 2013.06.13
I have performed some experiments with the gdata library in Py4a.
Finally I have get the following "quick&dirty" solution:
import gdata.tlslite.utils.keyfactory as rk
#Generate the recipient's RSA key
sec=rk.generateRSAKey(1024)
#obtain the publickey, which will be stored
#in the sender mobile phone
pubxml=sec.writeXMLPublicKey()
print pubxml
#Create the public key from XML
pub=rk.parseXMLKey(pubxml)
#
#Now lets simulate the sender
#It has only access to "pub"
#
import gdata.tlslite.utils.PyCrypto_AES as sk
import gdata.tlslite.utils.cipherfactory as cf
#Generate random key and initioalization vectors
key=sk.getRandomBytes(32)
iv=sk.getRandomBytes(16)
#Here we should check if the key and iv are reasonable
#Now we accept them as they are
#Text to encrypt
txt1="Strictly secret unknown text!"
#Pad the text to the length N*16
padlen=16-(len(txt1) % 16)
if padlen:
txt1=txt1.ljust(len(txt1)+padlen, " ")
#Create the AES key
ak=cf.createAES(key.tostring(),iv.tostring())
#Encrypt text
ctxt1=ak.encrypt(txt1)
#Encrypt key and initialization vector with recipients publickey
ckey1=pub.encrypt(key+iv)
#
# Now we simulate the recipient
# It has its secret key 'sec', and received encrypted key
# and iv from the sender in ckey1. It also receives ctxt1
#
pkey1=sec.decrypt(ckey1)
pkey=pkey1[0:32]
piv=pkey1[32:48]
# Now we decipher the text
pak=cf.createAES(pkey.tostring(),piv.tostring())
ptxt1=pak.decrypt(ctxt1)
# Print the deciphered text
print ptxt1
Probably this solution is far from being optimal, but it at least works.
Because the phone is an unsecure device, you cannot trust anything that is computed on the phone. If you want something done securely, do it on your server.
As for your question, this is just how public cryptography (at least RSA) works: you cannot encrypt any data that is longer than the key. The reason there are not libraries for this is that it is impossible. If you need secure email use S/MIME or GPG and don't try to reinvent the wheel. Also note that since the key needs to be in the app, anyone can extract it and decrypt your data. If you just want to send data securely, a better approach might be so send the data over HTTPS. Then you don't need to manage client keys and your data will be protected in transit.