Sultan
Sultan

Reputation: 148

How to solve android.security.KeyStore.getKeyStoreException in Android 11?

I am getting to many of below android security related exception/crash on my test enviourment

Device: Galaxy A52 5G Android: 11

Updated stack traces

ndroid.security.KeyStore.getKeyStoreException KeyStore.java:1441
android.security.KeyStore.getInvalidKeyException KeyStore.java:1548
android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit KeyStoreCryptoOperationUtils.java:54
android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit KeyStoreCryptoOperationUtils.java:89
android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized AndroidKeyStoreCipherSpiBase.java:265
android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit AndroidKeyStoreCipherSpiBase.java:109
javax.crypto.Cipher.tryTransformWithProvider Cipher.java:2984
javax.crypto.Cipher.tryCombinations Cipher.java:2891
javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider Cipher.java:2796
javax.crypto.Cipher.chooseProvider Cipher.java:773
javax.crypto.Cipher.init Cipher.java:1143
javax.crypto.Cipher.init Cipher.java:1084
au.com.gridstone.pscore.hkpf.data.providers.PushNotificationAlertEncryptionProvider.decrypt PushNotificationAlertEncryptionProvider.java:17
au.******package******.providers.PushNotificationAlertEncryptionProvider.decrypt PushNotificationAlertEncryptionProvider.java
au.c******package******.background.messaging.FirebaseCloudMessagingService.processMessage FirebaseCloudMessagingService.java:153
au.******package******.background.messaging.FirebaseCloudMessagingService.access$processMessage FirebaseCloudMessagingService.java
au.******package******.hkpf.background.messaging.FirebaseCloudMessagingService$onMessageReceived$1.invokeSuspend FirebaseCloudMessagingService.java:32
kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith BaseContinuationImpl.java:9
kotlinx.coroutines.DispatchedTask.run DispatchedTask.java:129
kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely CoroutineScheduler.java:1
kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask CoroutineScheduler.java:14
kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker CoroutineScheduler.java:28
kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run CoroutineScheduler.java

Backgorund In my project we are using RSA to send Push notification from Firbase to devices, server uses client public key to encrypt the notification data and client uses its private key to decrypt the notification after receiving it before displaying to users.

Code

fun retrieveKey(keyAlias: String): KeyPair? {
 val keyStore: KeyStore = KeyStore.getInstance("AndroidKeyStore")
    keyStore.load(null)

    val privateKey: PrivateKey? = keyStore.getKey(keyAlias, null) as? PrivateKey
    val publicKey: PublicKey? = keyStore.getCertificate(keyAlias)?.publicKey

    // If key is already found, return it
    if (privateKey != null && publicKey != null) {
      return KeyPair(publicKey, privateKey)
    }

    // Even if a key is not found, any records of a key alias must be purged
    keyStore.deleteEntry(keyAlias)

    // Generate it with keystore set to the provider, so that the provider takes care of storing it
    val generator: KeyPairGenerator =
        KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore")

    val keySpec: KeyGenParameterSpec = KeyGenParameterSpec
        .Builder(keyAlias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
        .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
        .setKeySize(1024)
        .build()

    generator.initialize(keySpec)


    val keyPair: KeyPair? = try {
      generator.generateKeyPair()
    } catch (exception: Throwable) {
      Crashes.trackError(exception)
      null
    }

    return keyPair
  }

usage To register a user device and sending a public key to server

fun register(pushToken: String, userCredentials: UserCredentials): Result<Unit> {
    
    val keyPair: KeyPair? = keyProvider.retrieveKey(PUSH_NOTIFICATIONS_KEY_ALIAS)

    val publicKeySpecFromEncoded: X509EncodedKeySpec? =
      keyPair?.public?.encoded?.let { X509EncodedKeySpec(it) }

    val pushKey: String? =
      publicKeySpecFromEncoded?.encoded?.let { String(Base64.encode(it, Base64.DEFAULT)) }

    val encryptionInstructions: EncryptionInstructions? =
      pushKey?.let { EncryptionInstructions(key = it) }

    val registration = DeviceRegistration(
      pushKey = pushKey.orEmpty(),
      pushToken = pushToken,
      encryptionInstructions = encryptionInstructions,
      deviceUuid = userCredentials.deviceUuid
    )

    return authWebServices.registerDevice(registration)
  }

And here is the code for function to decrypt the notification

decrypt(data: String, privateKey: Key?): PushNotificationData? {
    val cipher: Cipher = Cipher.getInstance(ENCRYPTION_CIPHER)
    cipher.init(Cipher.DECRYPT_MODE, privateKey)

    val encrypted: ByteArray = Base64.decode(data, Base64.DEFAULT)

    // This should only deal with RSA keys
    if (privateKey !is RSAKey) return null

    // Block size will limit how much we can decrypt at once, and specific to cipher
    val blockSize: Int = DECRYPTION_BLOCK_SIZE
    val chunks: List<ByteArray> = encrypted.chunked(blockSize)

    // Catch any errors when decrypting
    val decrypted: ByteArray = try {
      chunks.fold(ByteArray(encrypted.size)) { acc, byteArray ->
        acc + cipher.doFinal(byteArray)
      }
    } catch (t: Throwable) {
      Crashes.trackError(t)
      return null
    }
......
......
return pushNotificationData
}

I had tried puting this in try catch but it will silently fail the notification to crash. the app is working properly on android 12 and above.

Upvotes: 0

Views: 473

Answers (0)

Related Questions