java - Kotlin 使用内置 ECB Java 实现实现 AES-CBC
问题描述
我正在尝试按照此处指定的 CBC 模式的步骤,使用内置的 ECB Java 实现来实现 AES-CBC 密码。请注意,我并不关心我的实现的实际安全性(例如,没有填充,或使用密钥作为 IV)。
问题是,与使用PKCS5Padding
.
Key: mvLBiZsiTbGwrfJB
Input: abcdabcdabcdabcd
My result: e9qdKeY1m4OAIsPerfnUi5F35z814ywucLJKKi4rTP8=
Result from site: e9qdKeY1m4OAIsPerfnUi9jNsRJtdELZliFtebuJrrc=
Key: mvLBiZsiTbGwrfJB
Input: abcdabcdabcdabcdabcdabcdabcdabcd
My result: e9qdKeY1m4OAIsPerfnUi5F35z814ywucLJKKi4rTP8=uf5VPLwumm+66ESiQMlKXJF35z814ywucLJKKi4rTP8=
Result from site: e9qdKeY1m4OAIsPerfnUi7I+cPTpraAgZIQvr8OLf7Iu4eKRG1MIcq5yQGsRt3PS
对于NoPadding
选项:
Key: mvLBiZsiTbGwrfJB
Input: abcdabcdabcdabcd
My result: e9qdKeY1m4OAIsPerfnUiw==
Key: mvLBiZsiTbGwrfJB
Input: abcdabcdabcdabcdabcdabcdabcdabcd
My result: e9qdKeY1m4OAIsPerfnUiw==uf5VPLwumm+66ESiQMlKXA==
此外,解密根本不起作用,因为PKCS5Padding
我有例外:
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at java.base/com.sun.crypto.provider.CipherCore.prepareInputBuffer(CipherCore.java:1005)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:848)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202)
at AesEcb.decrypt(AesEcb.kt:27)
at AesEcb.decryptToByteArray(AesEcb.kt:36)
at AesCbcOwn.decrypt(AesCbcOwn.kt:32)
和NoPadding
:
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
at java.base/com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1109)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202)
at AesEcb.decrypt(AesEcb.kt:27)
at AesEcb.decryptToByteArray(AesEcb.kt:36)
at AesCbcOwn.decrypt(AesCbcOwn.kt:32)
我认为这些问题与填充选项有关,但无论选择何种选项,我仍然无法完成这项工作。
用于 CBC 实现的 AES-ECB 实现:
object AesEcb : Aes {
override val cipher: Cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
override fun encrypt(input: String, key: String): String {
val encrypted: ByteArray = try {
val secretKey = SecretKeySpec(key.toByteArray(), "AES")
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
cipher.doFinal(input.toByteArray())
} catch (e: InvalidKeyException) {
throw e
}
return Base64.getEncoder().run { encodeToString(encrypted) }
}
override fun decrypt(input: String, key: String): String {
val output: ByteArray = try {
val secretKey = SecretKeySpec(key.toByteArray(), "AES")
cipher.init(Cipher.DECRYPT_MODE, secretKey)
Base64.getDecoder().run { cipher.doFinal(decode(input)) }
} catch (e: InvalidKeyException) {
throw e
}
return String(output)
}
fun encryptToByteArray(bytes: ByteArray, key: String) = encrypt(String(bytes), key).toByteArray()
fun decryptToByteArray(bytes: ByteArray, key: String) = decrypt(String(bytes), key).toByteArray()
}
我的CBC实施:
object AesCbcOwn {
fun encrypt(plainText: String, key: String): String {
val iv = key.take(16).toByteArray()
val blocks = plainText.chunked(16).map { it.toByteArray() }
val encryptedBytes = with(blocks.iterator()) {
generateSequence(
AesEcb.encryptToByteArray(iv xor next(), key)
) {
try {
AesEcb.encryptToByteArray(it xor next(), key)
} catch (e: NoSuchElementException) {
null
}
}
}
return encryptedBytes.joinToString("") { String(it) }
}
fun decrypt(encryptedText: String, key: String): String {
val iv = key.take(16).toByteArray()
val encryptedBlocks = encryptedText.chunked(16).map { it.toByteArray() }
val decryptedBytes = with(encryptedBlocks.iterator()) {
generateSequence(
AesEcb.decryptToByteArray(next(), key) xor iv
) {
try {
AesEcb.decryptToByteArray(next(), key) xor it
} catch (e: NoSuchElementException) {
null
}
}
}
return decryptedBytes.joinToString("") { String(it) }
}
private infix fun ByteArray.xor(other: ByteArray) =
this.zip(other) { thisByte, otherByte -> thisByte xor otherByte }.toByteArray()
}
解决方案
您编写的 ECB 方法执行填充。这不应该发生:需要填充 CBC 明文,而不是提供给 AES 密码的块。目前,ECB 方法返回两个块而不是一个。
矢量未更新。IV(初始化向量)仅与初始明文块进行异或,然后最后一个密文块需要与下一个明文块进行异或。换言之,密文块成为下一个向量。
推荐阅读
- orocommerce - Oro PriceList 业务单位所有权
- vba - 更改 Visio 形状名称以匹配用户输入的形状数据
- c++ - 在 GCC 10.3.0 中找不到 _mm256_rem_epu64 内在函数
- html - 根据 created() 数据打开/关闭多个切换按钮
- javascript - Apexcharts - 在 jsfiddle 中运行 - 但不是我的 PC
- r - 如何填补将相同值与特定因素相关联的空白?R
- reflection - 在运行时修改 swagger 注释
- jquery - ajax成功后Jquery alert() 到模态
- powershell - Where-Object -InputObject 参数和管道之间的行为不一致
- python - VScode 中的 Jupyter Notebook 不导出 Pyplot 标题