Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Kotlin使用内置的ECB Java实现实现AES-CBC_Java_Kotlin_Encryption_Aes - Fatal编程技术网

Kotlin使用内置的ECB Java实现实现AES-CBC

Kotlin使用内置的ECB Java实现实现AES-CBC,java,kotlin,encryption,aes,Java,Kotlin,Encryption,Aes,我正在尝试使用内置的ECB Java实现来实现AES-CBC密码,方法是按照指定的CBC模式执行以下步骤。 注意,我并不关心我的实现的实际安全性(例如,没有填充,或者使用密钥作为IV) 问题是,与使用PKCS5Padding时的情况相比,只有部分字节编码正确 Key: mvLBiZsiTbGwrfJB Input: abcdabcdabcdabcd My result: e9qdKeY1m4OAIsPerfnUi5F35z814yw

我正在尝试使用内置的ECB Java实现来实现AES-CBC密码,方法是按照指定的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
对于
nop添加
选项:

Key:              mvLBiZsiTbGwrfJB
Input:            abcdabcdabcdabcd

My result:        e9qdKeY1m4OAIsPerfnUiw==


Key:              mvLBiZsiTbGwrfJB
Input:            abcdabcdabcdabcdabcdabcdabcdabcd

My result:        e9qdKeY1m4OAIsPerfnUiw==uf5VPLwumm+66ESiQMlKXA==
另外,解密根本不起作用,因为
pkcs5pdadding
我遇到了异常:

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)
对于
nop添加

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()
}
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()
}
我的CBC实施:

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()
}
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(初始化向量)仅与初始明文块异或,之后最后一个密文块需要与下一个明文块异或。换句话说,密文块成为下一个向量。

您编程的ECB方法执行填充。这不应该发生:CBC明文需要填充,而不是输入AES密码的块。当前,ECB方法返回两个块,而不是一个


向量不会更新。IV(初始化向量)仅与初始明文块异或,之后最后一个密文块需要与下一个明文块异或。换句话说,密文块成为下一个向量。

您的
aesebc.encryptToByteArray()
方法base64分别对每个块进行编码。这是不寻常的,您应该对所有组合的块进行编码。@t.m.adam和@Maarten Bodewes的答案都是正确的-我对所有组合的块进行了编码,将ECB密码更改为
NoPadding
,并修复了IV的问题,它成功了。我还为整个纯文本实现了PKCS5填充,而不是填充单个块。您的
aesebc.encryptToByteArray()
方法base64分别对每个块进行编码。这是不寻常的,您应该对所有组合的块进行编码。@t.m.adam和@Maarten Bodewes的答案都是正确的-我对所有组合的块进行了编码,将ECB密码更改为
NoPadding
,并修复了IV的问题,它成功了。我还为整个纯文本实现了PKCS5填充,而不是填充单个块。