so in my app i encrypt my data with AES key and encrypt that key with an RSA pair.
i then save that pair to the KeyStore.
when retrieving the private key from the KeyStore i need to format it to PKCS8
how ever i cannot format the private key because you cannot access private key encoded from key store.
is there a way to generate the pair in PKCS8 format? or maybe a workaround in order to format the private key without accessing the encoded?
generating the keys:
fun generateRSAKey(context: Context): KeyPair {
val keyPairGenerator = KeyPairGenerator.getInstance(
AppConst.RSA, "AndroidKeyStore"
)
val spec:AlgorithmParameterSpec
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M) {
spec = KeyGenParameterSpec.Builder(
"privateKey",
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setKeySize(1024)
.build()
}
else
{
val start: Calendar = Calendar.getInstance()
val end: Calendar = Calendar.getInstance()
end.add(Calendar.YEAR, 100)
spec = KeyPairGeneratorSpec.Builder(context)
.setAlias("privateKey")
.setSubject(X500Principal("CN=Sample Name, O=Android Authority"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.time)
.setEndDate(end.time)
.build()
}
keyPairGenerator.initialize(spec)
return keyPairGenerator.generateKeyPair()
}
getting the keys:
val keyStore=EncryptionUtil.getKeyStoreInstance()
keyStore.load(null)
val entry: KeyStore.Entry = keyStore.getEntry("privateKey", null)
val privateKey: PrivateKey = (entry as KeyStore.PrivateKeyEntry).privateKey
val decryptedAES = EncryptionUtil.decryptRSA(
android.util.Base64.decode(
aesKey,
android.util.Base64.DEFAULT
), KeyFactory.getInstance("RSA").generatePrivate(PKCS8EncodedKeySpec(privateKey.encoded))
)
Related
I want to generate a key and then use it to encrypt Room DB with SQLCipher, but the problem is SQLCipher requires a CharArray as a key to encrypt SQLite data. is there a way to use secretKey instead of CharArray or at least convert the secretKey to CharArray?.
My code to generate the key :
private val keyGenerator: KeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
private val keyGenParameterSpec = KeyGenParameterSpec.Builder(
KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build()
keyGenerator.init(keyGenParameterSpec)
keyGenerator.generateKey()
fun getKey(): SecretKey {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val secretKeyEntry = keyStore.getEntry(KEY_ALIAS, null) as KeyStore.SecretKeyEntry
return secretKeyEntry.secretKey
}
what is the best practice to create only one keypair for RSA encryption in all the android app cycle ?
I want to create the public key and the private key only once then use it whenever i want.
Yes you should create it one time with your alias key and next time before create new keystore object first you should check the same alias is already exist in keystore or not. If the alias is not exist in keystore then you should create new object. You can also check with the below code.
class AndroidKeyStore {
companion object {
private val CIPHER_TYPE = "RSA/ECB/PKCS1Padding"
private val CIPHER_PROVIDER = "AndroidOpenSSL"
private var keyStore: KeyStore? = null
const val KEY_ALIAS = "Keyalaisasfd"
}
init {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore?.load(null)
generateKey()
} catch (ex: Exception) {
}
}
#Throws(Exception::class)
private fun generateKey() {
// Create new key if needed
if (keyStore != null) {
if (!keyStore!!.containsAlias(KEY_ALIAS)) {
val kpg: KeyPairGenerator =
KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore")
val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.build()
kpg.initialize(parameterSpec)
kpg.generateKeyPair()
}
}
}
}
I have an Android app that uses Realm with encryption. We see very few cases of users getting an IllegalBlockSizeException during startup, when we setup the Realm encryption.
We create the key with the functions below:
private fun getOrGenerateKey() : ByteArray {
val keyPair = getOrGenerateKeyPair()
val prefs = applicationContext.getSharedPreferences(RealmPrefs, Context.MODE_PRIVATE)
val cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding") // We need an algorithm supported on all android API levels
// Try reading the encrypted AES key from the preferences
val key = prefs.getString(RealmPrefsAES, null);
if(key == null) {
// No key was stored, generate a new byte array, encrypt then store:
val keyBytes = ByteArray(64)
SecureRandom().nextBytes(keyBytes)
cipher.init(Cipher.ENCRYPT_MODE, keyPair.public)
val encryptedBytes = cipher.doFinal(keyBytes)
prefs.edit()
.putString(RealmPrefsAES, Base64.encodeToString(encryptedBytes, Base64.URL_SAFE))
.apply()
return keyBytes;
}
cipher.init(Cipher.DECRYPT_MODE, keyPair.private)
return cipher.doFinal(Base64.decode(key.trim(), Base64.URL_SAFE))
}
#SuppressLint("InlinedApi")
private fun getOrGenerateKeyPair() : KeyPair {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null);
val keyEntry = keyStore.getEntry(RealmKeyAlias, null) as KeyStore.PrivateKeyEntry?
if(keyEntry != null) {
return KeyPair(
keyEntry.certificate.publicKey,
keyEntry.privateKey
)
}
// If we got here, there was no key and we need to generate a new one. That is done differently in the API levels:
return if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore")
kpg.initialize(KeyGenParameterSpec.Builder(
RealmKeyAlias,
KeyProperties.PURPOSE_DECRYPT or KeyProperties.PURPOSE_ENCRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.build())
kpg.generateKeyPair()
} else {
val start = Calendar.getInstance()
val end = Calendar.getInstance()
end.add(Calendar.YEAR, 100)
#Suppress("DEPRECATION") // Deprecation handled by SDK version test
val spec = KeyPairGeneratorSpec.Builder(applicationContext)
.setAlias(RealmKeyAlias)
.setSubject(X500Principal("CN=$RealmKeyAlias"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.time)
.setEndDate(end.time)
.build()
val kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore")
kpg.initialize(spec)
kpg.generateKeyPair()
}
}
I'm trying to encrypt a arbitrary String using a KeyPair generated by an instance of java.security.KeyPairGenerator. Unfortunately after encrypting and decrypting the String with the generated KeyPair the result is incorrect.
here is how I go about doing this:
val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
load(null)
}
fun encryptUsingKey(publicKey: PublicKey, bytes: ByteArray): ByteArray {
val inCipher = Cipher.getInstance("RSA/NONE/NoPadding")
inCipher.init(Cipher.ENCRYPT_MODE, publicKey)
return inCipher.doFinal(bytes)
}
fun decryptUsingKey(privateKey: PrivateKey, bytes: ByteArray): ByteArray {
val inCipher = Cipher.getInstance("RSA/NONE/NoPadding")
inCipher.init(Cipher.DECRYPT_MODE, privateKey)
return inCipher.doFinal(bytes)
}
fun getKey(): KeyStore.Entry {
val containsAlias = ks.containsAlias(alias)
if (!containsAlias) {
val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
val parameterSpec: KeyGenParameterSpec =
KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_DECRYPT or KeyProperties.PURPOSE_ENCRYPT
)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setRandomizedEncryptionRequired(false)
.build()
kpg.initialize(parameterSpec)
val kp = kpg.generateKeyPair()
}
return ks.getEntry(alias, null)
}
My encryption/decryption test looks like this:
fun testEncryptionDecryption() {
val entry = getKey()
if (entry is KeyStore.PrivateKeyEntry) {
val privateKey = entry.privateKey
val certificate = entry.certificate
val publicKey = certificate.publicKey
val testKey = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
val encrypted = service.encryptUsingKey(publicKey, Base64.decodeFromString(testKey))
val decrypted = service.decryptUsingKey(privateKey, encrypted)
assertEquals(testKey, Base64.encodeToString(decrypted))
}
}
Unfortunately the result looks like this:
org.junit.ComparisonFailure: expected:<[0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF]> but was:<[AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANNdt-Oeu_PQAQgxBdNdt-Oeu_PQAQgxBdNdt-Oeu_PQAQgxBdNdt-Oeu_PQAQgxBQ]>
Can someone enlighten me to what's going on here? Where do all these A's come from? Am I using the keys incorrectly?
As suspected it was incorrect configuration. The following works:
val inCipher = Cipher.getInstance("RSA/ECB/OAEPPadding")
val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
val parameterSpec: KeyGenParameterSpec =
KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_DECRYPT or KeyProperties.PURPOSE_ENCRYPT
)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
.setDigests(KeyProperties.DIGEST_SHA1)
.build()
kpg.initialize(parameterSpec)
I want to secure the password used for Greendao db with SQLCipher. I am using a keystore to genrate a key and using that key encrypting a password getting from the server. I want to use encrypted data as a password for database.
The problem is cipher every time generating different encrypted data. (I know it is working as expected). Is there any way to generate the encrypted data similar every time? I am using following code for encryption: -
private fun generateSecureKey() {
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
val keyGenParameterSpec = KeyGenParameterSpec.Builder(BuildConfig.APPLICATION_ID,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build()
keyGenerator.init(keyGenParameterSpec)
keyGenerator.generateKey()
}
private fun getSecureKeyFromKeyStore(): SecretKey {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
return if (keyStore.containsAlias(BuildConfig.APPLICATION_ID)) {
val secretKeyEntry = keyStore.getEntry(BuildConfig.APPLICATION_ID, null) as KeyStore.SecretKeyEntry
secretKeyEntry.secretKey
} else {
generateSecureKey()
val secretKeyEntry = keyStore.getEntry(BuildConfig.APPLICATION_ID, null) as KeyStore.SecretKeyEntry
secretKeyEntry.secretKey
}
}
private fun encryptData(secureData: String): String {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, getSecureKeyFromKeyStore())
val bytes = cipher.doFinal(secureData.toByteArray())
return Base64.encodeToString(bytes, Base64.DEFAULT)
}