Android KeyStore Notes

Create PrivateKey/PublicCertificate Pair

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public void createNewKeys(View view) {
String alias = aliasText.getText().toString();
try {
// Create new key if needed
if (!keyStore.containsAlias(alias)) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 1);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
.setAlias(alias)
.setSubject(new X500Principal("CN=Sample Name, O=Android Authority"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair();
}
} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
refreshKeys();
}

Import PrivateKey/PublicCertificate Pair

The private key cannot be encrypted.
The password parameter of setKeyEntry has to be null.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// The key/certificate are the content between BEGIN and END in pem format
final private String PRIVATE_KEY = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCadLCqO4yNjdpZz26sTPXLx6JcgtNibHHldL//xRwZyYqX6udIbOH6Poau/niWEGeupBjN5qnCeDWTjNDBHGm+H+Se62V7PVH93O8bPRRoDzg4xZWrPhbDIZ6DlSvPh9/JakegNl4Nx7L9GVFbXYmFyqv6gziFjEbIGRBz9vW2QdbjxnmhnkiMobzI0Pxx/GMt1QZ92ezlsXUerECejVFStfr8SDlfZvKljIoVOJfZBBdTPh9zEi455dka+bPRv6w1v2SppNppC1jCargNcZ1dLr9ERKEh6hYdNP+qnRTeFZTAxIxiocRy0jvpvNs7bVP1PogOnK0ggl0khU9bontdAgMBAAECggEAUcVFYlp0ZtyapMF0zjNQTbo68s8wKgNPqTLOExK/dceFYDg0idtbJ6jClRKTtJ6qxYKKvzdG2HXbp2n9er8YHFe2KYxmBYDJT6UP0VPc4dps4WF5g1czpcq+qNrofs5oY7GoieE+mf0HfYdR0xUKIZLSyaV+3vUM5BKg60Prgbk3BcC79wDt/WlNT23vZrLop3KuW611v8DSMA73nHclWSh9kVcEcgctc3idprFfAjlF1pWybIHSgkOBd8XIJ94SBsMKqGKF5MRv2doRttUHz2kCw+MvdLRXm7ry/CzAqEwYL/KUMUrnzVN5JNllyB/Z/nZSIu8C0vsmPMWfCRIb4QKBgQDIChs4vKORSU8f2QXphTufD0EdtJJsM37AjJxhBM4EKQBreE0Ci0LBXlbdrodTE5JsjvNyL8S5jNOt79xwLq8/cpaBRXPoRkCNKckPQMlFWF4bFUjRAXSTuc0daF/4zqH8AJu4nLiBDm+ytrQlHDi/f6lLUO/2DgfT/rbx7gEg3wKBgQDFqhhSmr8P0NDK6c6+AoRTxhJcfcFxIi7Llad1CBPuxsvDn3Q8XpeI+QNfygrFPxi2g6GOnJQ8guGe9IUX2zY/RqJqEM6Pgc7zv0etCvC4jd6dW/ox0Td/ymOVltOIofsAQQ3SRNNqSgjin3KlOpyCw38J6NCevQXFeU0CPcA/QwKBgHGYS50MglYeqzAKrbDOV+0bXH7h68RF+dSeBK+Tauox3mspBZJMQrxMNkfstCwZES1UgCp3td8oeYxsiWGrIRmGmLEQH6HnQwDmmaZvw9v9MnNuwsrtaKX6/N+WRemkbZgNQGC1npCrAgMafXIVdKRKeniqnZm8nfVkz77SyRFBAoGBALr/H+OF4hrkQaK1bpDzcJyQVe6KSebtn7eZ7MIa0kCicUKwJxa1pkY+zaJhUa8o8gg4ny57kwFsEGaAjSj0iW1zNVOCcufwtgiCfRyHrRProx7bVOasNwT+QxfRiG1KUFr9MEYsNpXnVGCQ17TYrhhHOnf1eOezizI2f1QPd0BhAoGBAIDTegY9P1e1fzPALn9puHElapUXlXwZfiT9J1C6GkYywYwrXDkYXKXb+mvNIbL8YxKZ0ONecVxcmd693yh33VSGIngOvmGNhRmkY8EpTc2MsZXtu3Yej1Nn/q3y7X0+XWJ5YI3A1dCILjdfXAIlwb6wDIyxich8sgBEiEJotI1Y";
final private String UNSIGNED_CERT = "MIID7TCCAtWgAwIBAgIJAKvJ99aAhppSMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxETAPBgNVBAoMCFNlbGZ0ZXN0MRQwEgYDVQQLDAtTZWN0aW9uU2VsZjERMA8GA1UEAwwIdGVzdC5uZXQxHDAaBgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5uZXQwHhcNMTcwNDA0MDE1NTEyWhcNMTcwNTA0MDE1NTEyWjCBjDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MREwDwYDVQQKDAhTZWxmdGVzdDEUMBIGA1UECwwLU2VjdGlvblNlbGYxETAPBgNVBAMMCHRlc3QubmV0MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRlc3QubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmnSwqjuMjY3aWc9urEz1y8eiXILTYmxx5XS//8UcGcmKl+rnSGzh+j6Grv54lhBnrqQYzeapwng1k4zQwRxpvh/knutlez1R/dzvGz0UaA84OMWVqz4WwyGeg5Urz4ffyWpHoDZeDcey/RlRW12Jhcqr+oM4hYxGyBkQc/b1tkHW48Z5oZ5IjKG8yND8cfxjLdUGfdns5bF1HqxAno1RUrX6/Eg5X2bypYyKFTiX2QQXUz4fcxIuOeXZGvmz0b+sNb9kqaTaaQtYwmq4DXGdXS6/REShIeoWHTT/qp0U3hWUwMSMYqHEctI76bzbO21T9T6IDpytIIJdJIVPW6J7XQIDAQABo1AwTjAdBgNVHQ4EFgQUgQkNHWSC8zCUG5VvNK6+V0saxDYwHwYDVR0jBBgwFoAUgQkNHWSC8zCUG5VvNK6+V0saxDYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEHLEHBjmGbmE3CFtfStEHcvG1LIG8B96gBHKBY28XbdGQoxlZWlIfdsCjB80XP7S0Ku701N/Keh0kjDcEHp/TbOERwy4Icq/RdDnEj6Lbbc1I/c5w4mRu4KZPOpmdfeOPFBb2iFCpXw8iHUPCTnyQhkkL+kSy2bKxnrwj+MeRBudRhjdSWi3ZUKvxmC2LW4pMua9Y9MWANpl6/h9wLGRcicAmwZoUnHvLno9ccNc5DVq1Cbb73rpG1aMXoRdrcJqHmXMKC9g1cAKSuPfHgLASKKfVoLszv661r2yOI4MmiZKQhUHkbvt54F80P/t8AXpUTLDQ5btiQiUSF+IeoUQPg==";
final private String SIGNED_CERT = "MIID+zCCAuOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEUMBIGA1UECgwLU2Ftc3VuZ1Rlc3QxFDASBgNVBAsMC1NlY3Rpb25UZXN0MREwDwYDVQQDDAh0ZXN0Lm9yZzEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0Lm9yZzAeFw0xNzA0MDQwMjAwNDRaFw0xODA0MDQwMjAwNDRaMHQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UECgwGU2VsZkNBMRYwFAYDVQQLDA1TZWN0aW9uU2VsZkNBMREwDwYDVQQDDAh0ZXN0Lm5ldDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0Lm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJp0sKo7jI2N2lnPbqxM9cvHolyC02JsceV0v//FHBnJipfq50hs4fo+hq7+eJYQZ66kGM3mqcJ4NZOM0MEcab4f5J7rZXs9Uf3c7xs9FGgPODjFlas+FsMhnoOVK8+H38lqR6A2Xg3Hsv0ZUVtdiYXKq/qDOIWMRsgZEHP29bZB1uPGeaGeSIyhvMjQ/HH8Yy3VBn3Z7OWxdR6sQJ6NUVK1+vxIOV9m8qWMihU4l9kEF1M+H3MSLjnl2Rr5s9G/rDW/ZKmk2mkLWMJquA1xnV0uv0REoSHqFh00/6qdFN4VlMDEjGKhxHLSO+m82zttU/U+iA6crSCCXSSFT1uie10CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFIEJDR1kgvMwlBuVbzSuvldLGsQ2MB8GA1UdIwQYMBaAFODmZfUgqfPNVJEhF0ekF9Qh6La2MA0GCSqGSIb3DQEBCwUAA4IBAQBGnlZqIhC9l3wWYlO8AJ4toLbQnymkoknzOfMLCRCCHLtp0y23twb/Qp5D0D1bHb7VOKByqeiLCu3CFb8/nHAElqbcoFkmEvFv5QdxKefzRDCgj8KyR+teyJjekXHuaqZSPL1nETlvP+mRkEP4QGb1mvdmjZs3cMcIFJxD6MygEhIeJeUu2VyPAriVvt5Wk/4PEdJ6/ft5zSZyiCCI6nPyrPjixonDqnCmapbiYYqhGCRgaF02f3nQ61/T9KiZO8+nrh0tIKpjKz3A+qQgnF0kSjSW1MGZnHw2zxzk3AjSPCvXUvoCJKBj5Wgd1TFHzJaegaBDW9OSmJPiZgMZgyNO";

public void installPrivateKeyPair(View view) {
try{
PrivateKey privateKey = null;
Certificate[]chain = {null};
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(PRIVATE_KEY, Base64.DEFAULT)));

CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
chain[0] = certFactory.generateCertificate(new ByteArrayInputStream(Base64.decode(UNSIGNED_CERT, Base64.DEFAULT)));
keyStore.setKeyEntry("testAlias", privateKey, null, chain);
refreshKeys();
} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
}

// overwrite the unsigned certificate with the signed one, the private key remains the same
public void installSignedCert(View view) {
try{
PrivateKey privateKey = null;
Certificate[]chain = {null};
privateKey = (PrivateKey) keyStore.getKey("testAlias", null);

CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
chain[0] = certFactory.generateCertificate(new ByteArrayInputStream(Base64.decode(SIGNED_CERT, Base64.DEFAULT)));

keyStore.setKeyEntry("testAlias", privateKey, null, chain);
refreshKeys();
} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
}

Encrypt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void encryptString(String alias) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();

String initialText = startText.getText().toString();
if(initialText.isEmpty()) {
Toast.makeText(this, "Enter text in the 'Initial Text' widget", Toast.LENGTH_LONG).show();
return;
}

Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
inCipher.init(Cipher.ENCRYPT_MODE, publicKey);

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, inCipher);
cipherOutputStream.write(initialText.getBytes("UTF-8"));
cipherOutputStream.close();

byte [] vals = outputStream.toByteArray();
encryptedText.setText(Base64.encodeToString(vals, Base64.DEFAULT));
} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
}

Decrypt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void decryptString(String alias) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
PrivateKey privateKey = privateKeyEntry.getPrivateKey();

Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKey);

String cipherText = encryptedText.getText().toString();
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(cipherText, Base64.DEFAULT)), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte)nextByte);
}

byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}

String finalText = new String(bytes, 0, bytes.length, "UTF-8");
decryptedText.setText(finalText);

} catch (Exception e) {
Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
Log.e(TAG, Log.getStackTraceString(e));
}
}

Reference

Simple Android app showing how to use the Android Keystore to create, retrieve and delete Public/Private keypairs