【问题标题】:Java AES-256-CBC not working as expectedJava AES-256-CBC 未按预期工作
【发布时间】:2017-12-27 18:49:42
【问题描述】:

创建了一个新类以在 CBC 和 CTR 模式下使用 AES 进行测试。因此,使用此代码,CTR 工作正常,但 CBC 返回空数组。不知道为什么会这样,希望有人能解释一下。

import org.junit.Before;
import org.junit.Test;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import java.security.*;
import java.util.Map;

public class AES_retest {

    private static final String plaintext = "Hallo Welt";
    private static final String key = "C0BAE23DF8B51807B3E17D21925FADF2";
    private String iv_string = "I need a initialization vector...";
    private Cipher encrypt_cipher_ctr, decrypt_cipher_ctr, encrypt_cipher_cbc, decrypt_cipher_cbc;

    @Before
    public void prepare_Test() throws GeneralSecurityException {
        byte[] tmp = new byte[16];
        System.arraycopy(iv_string.getBytes(), 0, tmp, 0, 16);

        removeCryptographyRestrictions();

        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
        //initialization vector
        IvParameterSpec iv = new IvParameterSpec(tmp);
        encrypt_cipher_cbc = Cipher.getInstance("AES/CBC/PKCS5Padding");
        encrypt_cipher_cbc.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
        decrypt_cipher_cbc = Cipher.getInstance("AES/CBC/PKCS5Padding");
        decrypt_cipher_cbc.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);
    }

    @Test
    public void multiple_CBC_update() throws BadPaddingException, IllegalBlockSizeException {
        System.out.println("Testing CBC:");
        System.out.println("Plaintext: " + plaintext);
        System.out.println("Plaintext as HEX: " + bytesToHex(plaintext.getBytes()));
        byte[] first_encryption = encrypt_cipher_cbc.update(plaintext.getBytes());
        byte[] second_encryption = encrypt_cipher_cbc.update(plaintext.getBytes());
        encrypt_cipher_cbc.doFinal();
        byte[] first_decryption = decrypt_cipher_cbc.update(first_encryption);
        byte[] second_decryption = decrypt_cipher_cbc.update(second_encryption);
        decrypt_cipher_cbc.doFinal();
        System.out.println("First encryption: " + bytesToHex(first_encryption));
        System.out.println("Second encryption: " + bytesToHex(second_encryption));
        System.out.println("First decryption: " + bytesToHex(first_decryption));
        System.out.println("Second decryption: " + bytesToHex(second_decryption));
    }
}

因为我想在我的程序中同时使用 CTR 和 CBC,所以我想使用可以同时处理两者的 AES 实现。对于给定的实现,这可能吗?

我认为它与定义的填充有关。如果我在解密中使用 NoPadding,则密文会正确解密,但不会删除填充。如果我在解密时使用 PCS5Padding,则会返回一个空数组或 null。第一次加密也返回一个空数组...

Picture of what IntelliJ shows while debugging

按照 zaph 的建议添加了 doFinal 调用,现在我收到以下错误:

Testing CBC:
Plaintext: Hallo Welt
Plaintext as HEX: 48616c6c6f2057656c74

javax.crypto.BadPaddingException: Given final block not properly padded

at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:989)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:845)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2048)
at AES_retest.multiple_CBC_update(AES_retest.java:117)

在我的程序上下文中,我将通过加密的网络套接字发送和接收数据包。所以我需要独立地解密和加密每个数据包。我不想在每个数据包的末尾调用 doFinal,因为这意味着相同的数据包发送两次将以相同的方式加密。如果那是我想要的,那么我可以使用 ECB 模式。 ;)

例如 我想向 EchoServer 发送两次“Hello World”消息,服务器应该能够解密第一个接收到的数据包,而不会意识到后面还有更多数据包。此外,客户端应该能够加密和发送消息,不知道用户是否会提供应该发送的额外数据。 不确定在这种情况下必须如何以及在何处添加填充。

Cipher.doFinal 在每次调用后都会调用 Cipher.init,这很烦人。因此,如果我将“Hello World”加密两次并在每次加密后调用“doFinal”,那么两个数据包(如果每个“Hello World”都有自己的数据包)看起来都是一样的。

【问题讨论】:

  • 行号没有帮助。有效的额外代码 (CTR) 无济于事。提供一个minimal reproducible example,其中 minimum 仅表示失败的代码。提供十六进制的加密数据。您有 println 语句,但没有提供它们的输出。
  • 要加密的文本是 10 个字符,所以只有一个完全由 doFinal 生成的输出块,但是您在这之前进行了两次调用,这会耗尽输入,所以没有 @ 987654327@。 IV 是 33 个字符,但所需的 IV 只有 16 个字节。您需要对基于块的加密(例如 AES)进行一些研究。
  • 如果您认为 CTR 模式是存在问题的解决方案,主要的问题是 IV 不能永远不能重复使用与相同的键。
  • IV只有16个字节长:byte[] tmp = new byte[16]; System.arraycopy(iv_string.getBytes(), 0, tmp, 0, 16);在真正的程序中,密钥和 iv 是从 DH_key_exchange 派生的,所以这应该不是问题。我会尽量让问题更清楚。
  • private String iv_string = "I need a initialization vector..."; 33 字节。

标签: java encryption aes cbc-mode badpaddingexception


【解决方案1】:

你错过了encrypt_cipher_cbc.doFinal

发件人:Class Cipher - doFinal

byte[] doFinal() 完成多部分加密或解密操作,具体取决于如何 此密码已初始化。

byte[] doFinal(byte[] input) 在单部分操作中加密或解密数据,或完成多部分操作。

【讨论】:

  • 使用doFinal只有一个问题。我无法一次完成所有加密。因为这是在网络协议中使用的。使用 doFinal 将导致重置 IV,但下一次加密将再次使用“旧”参数。
  • 根据需要执行尽可能多的encrypt_cipher_cbc.update。然后encrypt_cipher_cbc.doFinaldoFinal 可以选择是否有一些最终文本。请参阅答案中的链接。由于 CBC 模式默认提供填充,因此它必须在最后进行最终调用,以便添加填充。 CTR 模式没有填充,因此无需调用doFinal
  • 您忽略了encrypt_cipher_cbc.doFinal() 的输出,即填充与最后一个块的数据一起创建的位置。
  • 这是否意味着我需要用结果更新我的 second_encryption byte[] ?但是,如果这是我想的那样,我怎么能将 CBC 用作网络用例呢?每个数据包都必须加密,但不应在加密两个不同数据包之间重置 IV。此外,接收方应该能够按原样解密数据包。
猜你喜欢
  • 2021-02-18
  • 1970-01-01
  • 1970-01-01
  • 2022-08-17
  • 1970-01-01
  • 2022-11-16
  • 2014-01-13
  • 2013-08-11
  • 1970-01-01
相关资源
最近更新 更多