Android Keystore's password protection - android

We used Anroid Keystore to store some confidential data and set up a password for Keystore. This passwords are used in conjunction with the KeyStore class in the load, getKey and setKeyEntry methods.
The Keystore itself is encrypted and app can only view and query its own data so we can say that data are somewhat secure inside Keystore but how we can secure the password that associated with keystore account? I found many example online and most of them having hardcoded password in code or use null parameter.
Please see in below example. I want to know what is the best approach to secure hardcoded password?
Want to find a safe way in android device itself to store this hardcoded password. Assume that moving it to external place like database, service call etc. options are NOT available.
Context context;
KeyStore ks;
KeyStore.PasswordProtection prot;
static readonly object fileLock = new object ();
const string FileName = "Xamarin.Social.Accounts";
static readonly char[] Password = "3295043EA18CA264B2C40E0B72051DEF2D07AD2B4593F43DDDE1515A7EC32617".ToCharArray ();
public AndroidAccountStore (Context context)
{
this.context = context;
ks = KeyStore.GetInstance (KeyStore.DefaultType);
**prot = new KeyStore.PasswordProtection (Password);**
try {
lock (fileLock) {
using (var s = context.OpenFileInput (FileName)) {
ks.Load (s, Password);
}
}
}
catch (FileNotFoundException) {
//ks.Load (null, Password);
LoadEmptyKeyStore (Password);
}
}

Assume that moving it to external place like database, service call etc. is NOT possible
You want to securely store sensitive information on the local user's machine.
The only way to do that is encrypting it. The most popular encryption algorithm is AES, and luckily Microsoft included an implementation of it in C#.
However, encryption uses a secret key to encrypt/decrypt the data, so we're basically moving the problem back - now we need to store that encryption key securely.
You could hard-code that key in the app, but a dedicated attacker could still get it and decrypt the password.
Instead, get that password from the user. Ask them to provide a password, hash it (using e.g. SHA256) and use the hash as the key for the encryption.

Related

Add RSA public key to Android KeyStore in Xamarin Forms

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

How Do I manage Android Keystore KMS for symmetric encryption and decryption?

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.

Storing symmetric key for offline usage

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.

User data in account manager is accessible by other applications

I am trying to utilize Android's Account Manager to store user's app credentials.
Although I am not saving the user's password, I wanted to save other security keys to the account's UserData. According to the documentation quoted below, this should NOT be accessible by applications with different UID.
public String getUserData (Account account, String key)
Gets the user data named by "key" associated with the account. This is intended for authenticators and related code to store arbitrary metadata along with accounts. The meaning of the keys and values is up to the authenticator for the account.
It is safe to call this method from the main thread.
This method requires the caller to hold the permission AUTHENTICATE_ACCOUNTS and to have the same UID as the account's authenticator.
Parameters
account - The account to query for user data
Returns
The user data, null if the account or key doesn't exist
To test this, I created an application that creates an account and saves some contents to UserData. I also created another application that accesses the accounts of the first app. Below are the snippets:
First app:
AccountManager am = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
final Account account = new Account("Account Name", "my.account.type");
am.addAccountExplicitly(account, null, null);
am.setAuthToken(account, "my.account.type", "some auth token");
am.setUserData(account, "key.for.secure.user.data", "some secure data");
Second app:
AccountManager am = (AccountManager)context.getSystemService(Context.ACCOUNT_SERVICE);
Account[] accountsFromFirstApp = am.getAccountsByType("my.account.type");
for(Account acct: accountsFromFirstApp){
printToLogs(acct.name);
printToLogs(am.getUserData(acct, "key.for.secure.user.data"));
}
Based on the documentation above, I expected the second app's getUserData() to return an exception for not having the same UID as the owner app. Surprisingly, I was able to access the user data of the first app with no errors.
But when I tried to access accounts from google by using "com.google" as the accountType, I got the expected exception.
What was wrong with my implementation? Did I miss some configuration that was not stated in Android's documentation? Any help will be very much appreciated.
On the second thought, If these user data can just be accessed that easily (assuming that the other applications know my app's account type), then what's the difference of storing strings in UserData from storing them in SharedPreferences instead?
From the AccountManager documentation:
This class provides access to a centralized registry of the user's online accounts. The user enters credentials (username and password) once per account, granting applications access to online resources with "one-click" approval.
Accounts managed by the AccountManager are centralized and reusable, e.g. all Google apps can use the same account, and not every app has to create its own Google account.
So one of the ideas of the AccountManager, as far as i understand, is to have reusable accounts, accessible from different apps.
But as the stored credentials are accessible from different places, you shouldn't store any plain text passwords in the AccountManager.
May this topic is interesting for you: What should I use AccountManager for?
If your data is Personal data, you can encrypt it, so no other apps can read them.
I.E using AES Encryption like this:
public class AESCryptor
{
private static final String ALGORITHM = "AES";
// 16-bit Key for encryption (Change to yours)
private static final String KEY = "XXXXXXXXXXXXXXX";
public static String encrypt(String value) throws Exception
{
Key key = generateKey();
#SuppressLint("GetInstance") Cipher cipher = Cipher.getInstance(AESCryptor.ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte [] encryptedByteValue = cipher.doFinal(value.getBytes("utf-8"));
return Base64.encodeToString(encryptedByteValue, Base64.DEFAULT);
}
public static String decrypt(String value) throws Exception
{
Key key = generateKey();
#SuppressLint("GetInstance") Cipher cipher = Cipher.getInstance(AESCryptor.ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedValue64 = Base64.decode(value, Base64.DEFAULT);
byte [] decryptedByteValue = cipher.doFinal(decryptedValue64);
return new String(decryptedByteValue,"utf-8");
}
private static Key generateKey() {
return new SecretKeySpec(AESCryptor.KEY.getBytes(),AESCryptor.ALGORITHM);
}
}
I use it in my apps, and it works!
This maybe doesn't answer the question
User data in account manager is accessible by other applications
But its answered by #thomas-s-e
Regards,
#developerfromjokela

Using Android's KeyChain API for exchanging public keys

I am trying to write an application that enables users to exchange their public (e.g. RSA) keys via Bluetooth/NFC. To store and later retrieve the keys, I would like to use Android's KeyChain API.
I have read the online API documentation and some tutorials/examples so far. They all involve importing a keychain from a PKCS12 file which contains a private key and the corresponding public key and certificate. Once this file is imported, an alias-String is returned for subsequent reference. So far, so good.
However, what I want to do is receive someone's public key, store it, get back an alias, store that alias somewhere (e.g. contacts data) and use it to retrieve back the public key when the user wants to encrypt a message to that person. Is that possible? I have very little experience with Public-key cryptography and have the feeling that I have misunderstood the whole purpose of the KeyChain API.
Any help would be much apprechiated! Thanks.
The Android KeyChain API is designed to store SSL certificates and keys: your own keys when installing a PKCS#12 or a trusted root certification authorities certificates. This keys and certificate are then available to all applications.
It depends on the purpose of the application you are developing but you maybe should consider using a KeyStore dedicated to your application to store the keys you received instead of the KeyChain API.
Another limitation of both API (KeyStore and KeyChain) is taht it is not possible to directly store public keys. You need to have a certificate. I suggest you to embedded a self-signed certificate in your application and use this certificate to sign "dummy" certificates containing the public keys the application will receive.
A simplified code snippet with the bouncycastle library to store a public RSA key:
public void storeRSAPublicKey(String alias, BigInteger modulus, BigInteger exponent)
{
/** Load the key to generate the certificate */
KeyStore ks = getApplicationKeyStore();
KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)ks.getEntry(MY_PRIVATE_KEY, null);
X509Certificate issuerCert = (X509Certificate)entry.getCertificate();
PrivateKey skey = entry.getPrivateKey();
/** Prepare the certificate template */
RSAKeyParameters params = new RSAKeyParameters(false, modulus, exponent);
SubjectPublicKeyInfo pkInfo = SubjectPublicKeyInfoFactory.SubjectPublicKeyInfo(params);
X500Name issuer = new X500Name(issuerCert.getIssuerX500Principal().getName());
X500Name subject = new X500Name("CN=alias");
X509v3CertificateBuilder builder = new X509v3CertificateBuilder(issuer, randomSeriaNumber(), new Date(), dateIn20years(), subject, pkInfo);
/** Generate the certificate */
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(skey);
X509CertificateHolder holder = builder.build(signer);
/** Store the certificate in the KeyStore */
JcaX509CertificateConverter conv = new JcaX509CertificateConverter();
X509Certificate cert = conv.getCertificate(holder);
ks.setCertificateEntry(alias, cert);
pushKeyStoreToPersistentStorage(ks);
Now you can get the certificate with ks.getCertificateEntry(alias);
Note that I omitted some non fundamental code like serial number generation and not-after date computation.
You can create the initial KeyStore with keytool and add it to you app as a resource.

Categories

Resources