SharedPreferences and Android KeyStore
We all know them and we’ve all used them, most of the time to store some flags and other times to store private user information (…bad devs ). But how many times do we think about security ?
I mean, the SharedPreferences are private to the app that created them (at least in theory) so why should I bother ? Because root access is no longer something only power users know of, because you shouldn’t be comfortable storing private user information in clear text !!
Ok, ok…I’ll just encode the values with Base64. Well, yeah, not bad, but what if you could do it better with the same amount of code you would write to encode them with Base64 ? Say hello to the Android KeyStore.
The Android KeyStore allows an application to store cryptographic keys that can’t be extracted from the application process or the Android device as a whole. But wait, I know what you’re thinking - I don’t want to ask the user to enter a key just to save some data and hard-coding the key in the app…that’s even worse. Agreed ! But here’s the best part, using the Android KeyStore Provider you can generate the keys without user interaction.
So, now we have a way to generate keys and store them securely, what do we do next ?
Here’s what we need to do :
1. Generate keys
Generating keys is pretty straightforward, we use the KeyPairGeneratorSpec to create a self-signed certificate providing an ALIAS, a start and end date.
KeyPairGeneratorSpec spec =
new KeyPairGeneratorSpec.Builder(context)
.setAlias(ALIAS)
.setSubject(new X500Principal("CN=" + ALIAS))
.setSerialNumber(BigInteger.valueOf(1337))
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
After we created the certificate, we use KeyPairGenerator to generate the actual keys. When we get an instance of the KeyPairGenerator we need to specify the algorithm and the keystore we want to use (in our case it’ RSA and the AndroidKeyStore).
final KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(TYPE_RSA, KEYSTORE);
kpGenerator.initialize(spec);
final KeyPair kp = kpGenerator.generateKeyPair();
2. Get key from keystore
The public and private keys are now created and saved in the keystore. Before we can actually use them, we need to obtain them:
KeyStore ks = KeyStore.getInstance(KEYSTORE);
ks.load(null);
// Load the key pair from the Android Key Store
KeyStore.Entry entry = ks.getEntry(ALIAS, null);
3. Use the force …errm the key
Now that we have an actual key, we can encrypt our values.
final KeyStore.PrivateKeyEntry privateKeyEntry = getPrivateKey(context);
if (privateKeyEntry != null) {
final PublicKey publicKey = privateKeyEntry.getCertificate().getPublicKey();
// Encrypt the text
Cipher input = Cipher.getInstance(CYPHER);
input.init(Cipher.ENCRYPT_MODE, publicKey);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, input);
cipherOutputStream.write(toEncrypt.getBytes(ENCODING));
cipherOutputStream.close();
byte[] vals = outputStream.toByteArray();
return Base64.encodeToString(vals, Base64.DEFAULT);
}
Hey look, we also use Base64 …and yes, I might have lied when I said it’s the same amount of code as just encoding them, but the good news is you write it once and then use it in all your projects.
Here’s a simple utility class that saves String preferences using the method described above.
Hope you find this useful, thanks for reading.