使用 AES 的对称加密在 cipher.init 上导致 NullPointerException

Symmetric encryption using AES causes NullPointerException on cipher.init

我正在尝试让 AES 对称加密在我的 android 应用程序中工作,但我很难让它工作。

下面的示例代码

Activity

override fun onCreate() {
  ...
  val key = KeyGenerator().generateSymemetricKey(ANDROID_KEYSTORE, "token", AES, PKCS7, CBC, PURPOSE_ENCRYPT or PURPOSE_DECRYPT)

  val encryptionPair = EncryptionHelper.encrypt(AES, "Testing", key)
  encryptionPair?.let {
    val decryptedString = EncryptionHelper.decrypt(AES, it.first, key, it.second)
    Timber.d(decryptedString)
  }
class KeyGenerator {
    fun generateSymemetricKey(keystoreName: String, alias: String, algorithm: String, padding: String, blockMode: String, purposes: Int): Key {

        val keystore = KeyStore.getInstance(keystoreName)
        keystore.load(null)

        if (!keystore.containsAlias(alias)) {
            val keyGenerator = KeyGenerator.getInstance(algorithm, keystoreName)


            val keyGenParameterSpec = KeyGenParameterSpec.Builder(alias, purposes)
                .setBlockModes(blockMode)
                .setEncryptionPaddings(padding)
                .build()

            keyGenerator.init(keyGenParameterSpec)

            return keyGenerator.generateKey()

        } else {

            return keystore.getKey(alias, null) as Key
        }
    }
}
object EncryptionHelper {

    fun encrypt(algorithm: String, text: String, key: Key): Pair<ByteArray, ByteArray>? = try {
        val cipher = Cipher.getInstance(algorithm)

        cipher.init(Cipher.ENCRYPT_MODE, key)

        val cipherText = cipher.doFinal(text.toByteArray())

        Pair(cipherText, cipher.iv)

    } catch (exception: GeneralSecurityException) {

        null
    }

    fun decrypt(algorithm: String, cipherText: ByteArray, key: Key, iv: ByteArray): String? = try {
        val cipher = Cipher.getInstance(algorithm)

        cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv))

        val plainText = cipher.doFinal(cipherText).toString()

        plainText

    } catch (exception: GeneralSecurityException) {
        null
    }
}

encrypt中调用cipher.init时,BouncyCastle抛出如下异常

2020-01-09 17:05:13.743 24911-24911/com.smartrent.alloytile E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.smartrent.alloytile, PID: 24911
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.smartrent.alloytile/com.smartrent.alloytile.MainActivity}: java.lang.NullPointerException: Attempt to get length of null array
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3092)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3235)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1926)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:6986)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)
     Caused by: java.lang.NullPointerException: Attempt to get length of null array
        at com.android.org.bouncycastle.crypto.params.KeyParameter.<init>(KeyParameter.java:13)
        at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(BaseBlockCipher.java:692)
        at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(BaseBlockCipher.java:1076)
        at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
        at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
        at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
        at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
        at javax.crypto.Cipher.init(Cipher.java:1143)
        at javax.crypto.Cipher.init(Cipher.java:1084)
        at com.smartrent.crypto.EncryptionHelper.encrypt(EncryptionHelper.kt:17)
        at com.smartrent.alloytile.MainActivity.onCreate(MainActivity.kt:62)
        at android.app.Activity.performCreate(Activity.java:7326)
        at android.app.Activity.performCreate(Activity.java:7317)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3072)

在调试错误时,我发现 key.encoded 为空,这就是最终导致问题的原因。然而,这是我发现的所有教程处理对称加密的方式,似乎没有人遇到这个问题。

在发布的代码中,在 Android 密钥库系统中生成一个密钥,用于使用 CBC 模式和 PKCS7 填充的 AES 加密。对于此星座,"AES/CBC/PKCS7Padding" 必须作为参数传递给 Cipher.getInstance(),如 Android 密钥库系统支持的此 list 密码中指定的那样。