Hi @nsdestr0yer ,
thanks for the tutorial.
Do you have any hint on how tu create a new secret key on Android versions prior to 23? Unfortunately KeyGenParameterSpec was added on that version of Android.
Thanks, Francesco
@francesco_pedron-omn Yes, AES symmetric keys are not supported but RSA asymmetric keys are so you could use that. The main difference here is that an asymmetric keypair contains two keys, a private and a public key, where the public key encrypts the data and the private key decrypts it. So in this case you’d use a KeyPairGeneratorSpec - passed into the KeyPairGenerator that is initialized with KEY_ALGORITHM_RSA and the “AndroidKeyStore” provider. To encrypt, you’d get the RSAPublicKey from the keypair. Decryption is done using the RSAPrivateKey object. I can paste a quick and dirty example…
@francesco_pedron-omn rough example might be:
private fun testPreMEncryption() {
try {
//Generate a keypair and store it in the KeyStore
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val start = Calendar.getInstance()
val end = Calendar.getInstance()
end.add(Calendar.YEAR, 10)
val spec = KeyPairGeneratorSpec.Builder(this)
.setAlias("MyKeyAlias")
.setSubject(X500Principal("CN=MyKeyName, O=Android Authority"))
.setSerialNumber(BigInteger(1024, Random()))
.setStartDate(start.time)
.setEndDate(end.time)
.setEncryptionRequired() //on API level 18, encrypted at rest, requires lock screen to be set up, changing lock screen removes key
.build()
val keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore")
keyPairGenerator.initialize(spec)
keyPairGenerator.generateKeyPair()
//Encryption test
val encryptedBytes = rsaEncrypt("My secret string!".toByteArray(charset("UTF-8")))
val decryptedBytes = rsaDecrypt(encryptedBytes!!)
val decryptedString = String(decryptedBytes!!, Charsets.UTF_8)
Log.e("MyApp", "Decrypted string is $decryptedString")
} catch (e: Throwable) {
e.printStackTrace()
}
}
fun rsaEncrypt(decryptedBytes: ByteArray): ByteArray? {
var encryptedBytes: ByteArray? = null
try {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val privateKeyEntry = keyStore.getEntry("MyKeyAlias", null) as KeyStore.PrivateKeyEntry
val publicKey = privateKeyEntry.certificate.publicKey as RSAPublicKey
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val outputStream = ByteArrayOutputStream()
val cipherOutputStream = CipherOutputStream(outputStream, cipher)
cipherOutputStream.write(decryptedBytes)
cipherOutputStream.close()
encryptedBytes = outputStream.toByteArray()
} catch (e: Throwable) {
e.printStackTrace()
}
return encryptedBytes
}
fun rsaDecrypt(encryptedBytes: ByteArray): ByteArray? {
var decryptedBytes: ByteArray? = null
try {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val privateKeyEntry = keyStore.getEntry("MyKeyAlias", null) as KeyStore.PrivateKeyEntry
//val privateKey = privateKeyEntry.privateKey as RSAPrivateKey
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.DECRYPT_MODE, privateKeyEntry.privateKey)
val cipherInputStream = CipherInputStream(ByteArrayInputStream(encryptedBytes), cipher)
val arrayList: ArrayList<Byte> = ArrayList()
var nextByte: Int
do {
nextByte = cipherInputStream.read()
if (nextByte == -1) {
break
}
arrayList.add(nextByte.toByte())
} while (true)
decryptedBytes = ByteArray(arrayList.size)
for (i in decryptedBytes.indices) {
decryptedBytes[i] = arrayList[i]
}
} catch (e: Throwable) {
e.printStackTrace()
}
return decryptedBytes
}
Thank you very much.
To other people with same requirements I’ve found also useful this series of articles from Yakiv Mospan
This tutorial is more than six months old so questions are no longer supported at the moment for it. Thank you!