【问题标题】:Java AES cipher text sizeJava AES 密文大小
【发布时间】:2013-04-08 13:51:24
【问题描述】:

我正在使用一种非常标准的 Java AES 加密/解密方式。

byte[] key = hexStringToByteArray("C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF");

byte[] message = hexStringToByteArray("01A0A1A2A3A4A5A6A703020100060001");

SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

byte[] encrypted = cipher.doFinal(message);

cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] original = cipher.doFinal(encrypted);

如您所见,我使用的是 128 位密钥和 128 位消息。我几乎总是得到我期望的结果,但是,加密的结果总是 256 位长。第二个 128 位始终相同。除了截断结果,如何确保密码只返回前 128 位,而不更改前 128 位?我觉得我在这里对块大小的定义有点混淆了。

【问题讨论】:

    标签: java cryptography aes block encryption


    【解决方案1】:

    在处理您的问题之前,有一些强制性的事情需要解决。我在这段代码中看到的有潜在危险的一件事是密码没有使用显式模式和填充指定。这意味着它取决于提供者的默认值。如果这是当前与 Oracle 的 JVM 一起分发的提供程序,则这些默认值为 ECBPKCS5Padding。如果这些是您想要使用的,请以这种方式指定它们:

     Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    

    第二个是ECB 不是一个好的模式选择,因为它不是很安全。 CBC 是一个更好的选择,因为它使用了初始化向量。

    关于这个问题。加密文本大小的原因是由于填充方案,在本例中为PKCS5。填充是必要的,以确保纯文本具有算法可以处理的长度。对于 AES,它必须是 16 字节的倍数。在未加密数据的长度已经是 16 字节的倍数的情况下,填充必须添加额外的 16 字节(请参阅此处的 jbtule 注释)。像这样初始化Cipher会产生16字节的加密数据:

    Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
    

    这要求未加密数据的长度已经是 16 字节的倍数,因为它根本不会填充它。如果不是,则抛出异常。

    确切地了解您想要做的事情以提供好的建议可能会有所帮助。

    【讨论】:

    • 只是为了澄清您非常正确的答案,PKCS5 填充将始终在密文恰好是块大小时添加一个完整的块填充,而不管实现如何。否则你将如何从填充中确定密文。
    • 对,这很有道理。
    • 我需要它以这种方式工作,我正在尝试使用它来加密 CCM 块,因此块大小和加密数据的大小始终相同。我需要默认实现的原因是因为我需要编写一些单元测试,并且似乎使用了这个密码,因为我得到了完全相同的结果。谢谢你的回答,对我帮助很大!
    • 您可能需要考虑将CTRZim-Zam O'Pootertoot 提到的初始化向量一起使用,因为它可以处理任何长度的数据而无需填充。
    【解决方案2】:

    您的密码实例正在使用 PKCS5Padding 填充,它会在密文中添加最多 16 个字节的填充。有几种方法可以纠正这个问题:

    选项 1:不使用使用填充的 Cipher.getInstance("AES"),而是使用 Cipher.getInstance("AES/CBC/NoPadding")。但是,不建议这样做,因为它要求明文是 16 字节的倍数。

    选项 2:使用 BouncyCastle 作为您的加密提供商,然后使用

    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    Cipher.getInstance("AES/CTR/NoPadding", new BouncyCastleProvider());
    

    初始化密码。这使用计数器模式 (CTR) 而不是密码块链接模式 (CBC),并且计数器模式不需要任何填充。在计数器模式下,使用唯一的初始化向量很重要,它可以与密文一起以明文形式传输;例如,

    byte[] IV = new byte[16];
    new SecureRandom().getBytes(IV);
    cipher.init(Cipher.ENCRYPT_MODE, key, IV);
    

    然后在解密密文时,用相同的初始化向量初始化密码。如何传输 IV 取决于您,但同样,它不需要保密。

    密码块链接模式的初始化向量也应该是唯一的,但这并不像计数器模式那样重要。

    【讨论】:

    • 使用 BouncyCastle 不需要使用 CTR,因此即使使用 Oracle 的提供程序也可以。
    猜你喜欢
    • 2015-03-13
    • 2017-05-04
    • 2014-01-13
    • 2016-03-30
    • 2011-08-03
    • 1970-01-01
    • 2011-03-18
    • 2015-02-20
    • 2013-12-16
    相关资源
    最近更新 更多