BadPaddingException:由于java版本不同,给定的最后一个块没有正确填充

BadPaddingException:由于java版本不同,给定的最后一个块没有正确填充,java,encryption,aes,badpaddingexception,Java,Encryption,Aes,Badpaddingexception,我在客户端安装了Java8,我正在使用下面的技术加密我的数据文件 Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, key); outputStream = new CipherOutputStream(new FileOutputStream(encryptedFile), cipher); 现在我在服务器端解密,我已经按照下面的代码安装了Java7 Cipher c

我在客户端安装了Java8,我正在使用下面的技术加密我的数据文件

    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, key);
    outputStream = new CipherOutputStream(new FileOutputStream(encryptedFile), cipher);
现在我在服务器端解密,我已经按照下面的代码安装了Java7

Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
inputStream = new CipherInputStream(new FileInputStream(encryptedFile), cipher);
outputStream = new FileOutputStream(decryptedFileName);
这样做给我下面的错误

Caused by: java.io.IOException: javax.crypto.BadPaddingException: Given final block not properly padded
    at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:115) [jce.jar:1.7.0_71]
    at javax.crypto.CipherInputStream.read(CipherInputStream.java:233) [jce.jar:1.7.0_71]
    at javax.crypto.CipherInputStream.read(CipherInputStream.java:209) [jce.jar:1.7.0_71]
当我在两侧安装了相同的java版本(1.7)时,相同的代码可以正常工作。
我们如何解决这一问题,以便在不更改java版本的情况下,任何一方都有可能导致此问题的原因:

  • 您没有指定如何获取/生成密钥。如果您的JRE在拥有/不拥有方面存在差异,则一个JRE将支持256位AES加密,而另一个JRE仅支持128位AES加密。如果根据可用密钥长度生成密钥,则可能会导致密钥不匹配。同时,两个Java 7环境可能安装了相同级别的策略

  • 您没有在系统的任何一侧指定或填充方案——我建议使用AEAD模式,如
    GCM
    EAX
    、或
    CCM
    (CTR+CBC-MAC)以及
    NoPadding
    ,但是,即使是
    CBC/PKCS5Padding
    CTR/NoPadding
    也比默认的
    AES/ECB/PKCS5Padding
    要好,只需调用
    Cipher.getInstance(“AES”)

  • 在保存密码文本并对其进行反序列化以进行解密之前,您不会解释如何对密码文本进行编码。如果没有像十六进制或Base64这样的安全编码方案,使用原始二进制值时可能会遇到编码问题

  • 一旦您从
    ECB
    切换到另一种操作模式,您将需要为加密和解密提供初始化向量(IV),并将IV与密码文本一起传输。IV不需要以任何方式进行加密,但对于使用相同密钥加密的每条消息,它必须是唯一的且不可预测的。由于它始终是密码的块大小(AES固定为16字节/128位),只需在密文前面加上IV值,然后将其拆分以进行解密

  • AES(以及所有对称加密)使用相同的密钥进行加密和解密——不涉及公钥和私钥。这可能只是一个命名问题,但您试图使用
    publicKey
    解密的事实可能表明使用了错误的密钥。您应该验证加密密钥和解密密钥是否相同(长度相同(16、24或32字节)且相等)
    ECB
    如果密码文本是块大小(16字节)的精确倍数,“解密”将始终“成功”。然后验证填充。如果试图用错误的密钥解密消息,通常(255/256次)会出现填充错误。另一种情况是最后一个字节解密为
    0x01
    ,这是PKCS#5/#7的有效填充值,因此它不会检测到填充错误

  • 演示
    AES/ECB/PKCS5Padding
    是Java 8(1.8.0_101)上的默认值:


    Post I/O代码可能重复。
    @Test
    public void testCipherGetInstanceShouldDefaultToECB() throws Exception {
        // Arrange
        final String PLAINTEXT = "This is a plaintext message."
        final SecretKey key = new SecretKeySpec(Hex.decodeHex("0123456789ABCDEFFEDCBA9876543210" as char[]), "AES")
    
        Cipher unspecified = Cipher.getInstance("AES")
        final Cipher EXPECTED_CIPHER = Cipher.getInstance("AES/ECB/PKCS5Padding")
    
        unspecified.init(Cipher.ENCRYPT_MODE, key)
        EXPECTED_CIPHER.init(Cipher.DECRYPT_MODE, key)
    
        // Act
        byte[] cipherBytes = unspecified.doFinal(PLAINTEXT.getBytes(StandardCharsets.UTF_8))
        logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)}")
    
        // Assert
        byte[] recoveredBytes = EXPECTED_CIPHER.doFinal(cipherBytes)
        String recovered = new String(recoveredBytes, StandardCharsets.UTF_8)
        assert recovered == PLAINTEXT
    }