I have an app that generates a key for encryption/decryption and it is working just fine. I store my key in KeyStore and IV as first 12B in encrypted file saved on external storage. When I want to decrypt the file, I get the file from external storage (hence I get IV) and key from KeyStore, and I am able to get original content. My second application App2 can access file in external storage (hence it can get IV), but it can't get key from App1 KeyStore. I was reading about KeyChain and it says in official documentation it is not app private (Use the KeyChain API when you want system-wide credentials). Can I somehow store my key in this KeyChain or somewhere else so my App2 can get it (with some user approval or something similar). Here is the code I used to create and store key in App1.
private static SecretKey createAndStoreKey() {
KeyGenerator keyGen;
try {
// Generate 256-bit key
keyGen = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE_NAME);
final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
keyGen.init(keyGenParameterSpec);
SecretKey secretKey = keyGen.generateKey();
if(secretKey != null)
return secretKey;
else
return null;
}
catch (NoSuchProviderException e){
e.printStackTrace();
return null;
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
catch (InvalidAlgorithmParameterException e){
e.printStackTrace();
return null;
}
}
Thank you all for the help.
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.
Use the Android Keystore provider to let an individual app store its
own credentials that only the app itself can access. This provides a
way for apps to manage credentials that are usable only by itself
while providing the same security benefits that the KeyChain API
provides for system-wide credentials. This method requires no user
interaction to select the credentials.
refrence
Related
My application uses an RSA public key to encrypt the data before passed to the server. Everything works fine if I embed the public key to my solution and just use it.
However my client wants to change this public key sometimes, so I'm unable to add the PublicKey.key file to the solution as embedded resource.
My first tought was that I should install this public key to the Keystore like I install an X509Cetificate under the settings -> security -> Install user certificate method on the phone.
This method fails, because my file only contains the public key, which is not a certificate.
How can I store this public key in the Keystore? My goal is to install it, after that in would like to read it with my Xamarin Forms app in order to encrypt the data before I post to the server.
So my goal is not the set this public key to the keystore with code, it has to be there.
You could try to save public key as string into Secure Storage.
To save a value for a given key in secure storage:
try
{
await SecureStorage.SetAsync("oauth_token", "secret-oauth-token-value");
}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on device.
}
To retrieve a value from secure storage:
try
{
var oauthToken = await SecureStorage.GetAsync("oauth_token");
}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on device.
}
For more details, refer to Xamarin.Essentials: Secure Storage:
https://learn.microsoft.com/en-us/xamarin/essentials/secure-storage?context=xamarin%2Fandroid&tabs=ios
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 have access token from the server after authentication lets say "uyhjjfjfgg567f8fhjkkf" now I want to save it in the device securely. I looked in Keystore and Keychain in android developer sites. I dont clearly understand how it works and how we should retrieve the token from the keystore.
KeyPairGenerator kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA512)
.build());
KeyPair kp = kpg.generateKeyPair();
/*
* Load the Android KeyStore instance using the the
* "AndroidKeyStore" provider to list out what entries are
* currently stored.
*/
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
You don't need to save the access token, since it has short life anyway. Keeping it in memory is good enough.
You do need to keep the refresh token, and you have a few options for that:
In a file
Either directly in a file in the internal storage
or using SharedPreferences
or in a Database
Using the AccountManager
Consider using the StoredCredential. For the flow itself, I recommend you to use Google AppAuth library.
Of course, you can also encrypt the key using a cipher:
private static byte[] encrypt(byte[] key, byte[] text) throws GeneralSecurityException {
final SecretKeySpec skeySpec = new SecretKeySpec(key, KEY_ALGORITHM);
final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, sInitVectorSpec);
return cipher.doFinal(text);
}
And the key can be stored in the KeyStore.
We use a custom SharedPreference instance that encrypts the keys and values when adding, and decrypts when requesting.
SecurePreferences preferences = ...
preferences.edit().putString( "key", "value" ).apply(); // key and value are encrypted automatically
String value = preferences.getString( "key", null ); // key and value are decrypted automatically
I would only recommend using SharedPreferences if the values are encrypted, because even though the xml file is only available to the app, it can be accessed on rooted devices.
If you already using a SqlLiteDB, I would probably use that. If not, it's bit heavy for just saving a token.
EDIT:
An oauth token is completely unrelated to the key and keystore used to sign the app.
The oauth token is a token provided by the server after validating the user's credentials, within the app.
The keystore contains 1 or more certificates that is used to digitally sign the app. This is to prevent someone else from uploading an app that has the same package name as yours and replacing it.
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 am building an Android application that communicates with an online webservice. I plan on releasing the application's source code on GitHub. For my production version, which will utilize my personal webservice I want to allow only my digitally signed apk to connect.
Is is possible to request the APK's keystore and confirm the username/password from that keystore?
If this is not possible how else can I produce this functionality?
Edit:
I have read into the class Certificate It looks like I might be able to user public/private keys to confirm an identity. But I am still unsure of an implementation
I use this --
static public String getPackageFingerPrint( Context ctx ) {
PackageManager pm = ctx.getPackageManager();
String packageName = ctx.getPackageName();
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, flags);
} catch (NameNotFoundException e) {
return "";
}
Signature[] signatures = packageInfo.signatures;
byte[] cert = signatures[0].toByteArray();
InputStream input = new ByteArrayInputStream(cert);
CertificateFactory cf = null;
try {
cf = CertificateFactory.getInstance("X509");
} catch (CertificateException e) {
return "";
}
X509Certificate c = null;
try {
c = (X509Certificate) cf.generateCertificate(input);
} catch (CertificateException e) {
return "";
}
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(c.getPublicKey().getEncoded());
StringBuffer hexString = new StringBuffer();
for (int i=0;i<publicKey.length;i++) {
String appendString = Integer.toHexString(0xFF & publicKey[i]);
if(appendString.length()==1)hexString.append("0");
hexString.append(appendString);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e1) {
return "";
}
}
The problem I see with your approach is that anyone could determine the package fingerprint or your package and send it to your web-service. A better possibility would be to use a challenge-response mechanism: Your web-service sends you a unique session-token, which your app encrypts or digests using a shared algorithm, and then sends this encrypted token back to your service for verification. Of course, you wouldn't want to publish that algorithm to github.
You cannot restrict a web service to apks signed by a specific key.
The signature of your apk gets validated by the Android OS, and is not directly shared with web services the device connects to. Even if you read your signature from the keystore and send it along with requests, an attacker could just send the same signature (e.g. the same byte stream) without having access to your private key. He would just need to grab your signed apk, read the bytes from the keystore (or listen to a legitimate request) and spoil the data.
You would need to sign individual requests to have a level of security. But if you keep a private key in release versions (the key not distributed on gitHub), and sign requests using that key you are not safe as the private key is distributed as part of your apk and thus can get extracted easily.
In any way your API could get accessed by other apks.
However, there might be another way to restrict your API, for example by using license tokens, etc. In that case you would probably not care if the user builds the apk by himself, as long as he has a valid license token. If a license token is exploited and distributed, you could react to a high amount of traffic on that license token and for example block it from further requests. As I don't use Google Play I'm not sure in how far they can get validated from your server, but maybe the application licensing portal is a good starting point to search for suitable tokens.