使用 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 密码中指定的那样。
我正在尝试让 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 密码中指定的那样。