java 给定的最终块未正确填充

n1bvdmb6  于 2023-03-21  发布在  Java
关注(0)|答案(7)|浏览(165)

我尝试实现基于密码的加密算法,但遇到以下异常:
javax.crypto.BadPaddingException:给定的最终块未正确填充
有什么问题吗?
下面是我的代码:

public class PasswordCrypter {

    private Key key;

    public PasswordCrypter(String password)  {
        try{
            KeyGenerator generator;
            generator = KeyGenerator.getInstance("DES");
            SecureRandom sec = new SecureRandom(password.getBytes());
            generator.init(sec);
            key = generator.generateKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public byte[] encrypt(byte[] array) throws CrypterException {
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch (Exception e) { 
            e.printStackTrace();
        }
        return null;
    }

    public byte[] decrypt(byte[] array) throws CrypterException{
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch(Exception e ){
            e.printStackTrace();
        }
        return null;
    }
}

(The JUnit测试)

public class PasswordCrypterTest {

    private static final byte[] MESSAGE = "Alpacas are awesome!".getBytes();
    private PasswordCrypter[] passwordCrypters;
    private byte[][] encryptedMessages;

    @Before
    public void setUp() {
        passwordCrypters = new PasswordCrypter[] {
            new PasswordCrypter("passwd"),
            new PasswordCrypter("passwd"),
            new PasswordCrypter("otherPasswd")
        };

        encryptedMessages = new byte[passwordCrypters.length][];
        for (int i = 0; i < passwordCrypters.length; i++) {
            encryptedMessages[i] = passwordCrypters[i].encrypt(MESSAGE);
        }
    }

    @Test
    public void testEncrypt() {
        for (byte[] encryptedMessage : encryptedMessages) {
            assertFalse(Arrays.equals(MESSAGE, encryptedMessage));
        }

        assertFalse(Arrays.equals(encryptedMessages[0], encryptedMessages[2]));
        assertFalse(Arrays.equals(encryptedMessages[1], encryptedMessages[2]));
    }

    @Test
    public void testDecrypt() {
        for (int i = 0; i < passwordCrypters.length; i++) {
            assertArrayEquals(MESSAGE, passwordCrypters[i].decrypt(encryptedMessages[i]));
        }

        assertArrayEquals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[1]));
        assertArrayEquals(MESSAGE, passwordCrypters[1].decrypt(encryptedMessages[0]));

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[2])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[2].decrypt(encryptedMessages[1])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }
    }
}
nkkqxpd9

nkkqxpd91#

如果您尝试使用错误的密钥解密PKCS 5填充的数据,然后取消填充(这是由Cipher类自动完成的),您很可能会得到BadPaddingException(可能略小于255/256,大约99.61%),因为填充有一个特殊的结构,在取消填充期间进行验证,很少有密钥会产生有效的填充。

所以,如果你得到这个异常,抓住它,并把它当作“错误的键”。

当您提供错误的密码时,也会发生这种情况,然后使用该密码从密钥存储库获取密钥,或者使用密钥生成函数将其转换为密钥。
当然,如果数据在传输过程中损坏,也可能发生填充错误。
也就是说,关于您的方案有一些安全注意事项:

  • 对于基于密码的加密,您应该使用SecretKeyFactory和PBEKeySpec,而不是使用SecureRandom和KeyGenerator。原因是SecureRandom在每个Java实现上可能是不同的算法,为您提供不同的密钥。SecretKeyFactory以定义的方式进行密钥派生(如果您选择正确的算法,则认为该方式是安全的)。
  • 不要使用ECB模式。它独立加密每个块,这意味着相同的纯文本块也总是给予相同的密文块。

最好使用安全的mode of operation,如CBC(密码块链接)或CTR(计数器)。或者,使用还包括身份验证的模式,如GCM(伽罗瓦计数器模式)或CCM(带CBC-MAC的计数器),见下一点。

  • 你通常不仅需要保密性,还需要身份验证,这可以确保消息不被篡改。(这也可以防止对你的密码的选择密文攻击,即有助于保密性。)所以,在你的消息中添加MAC(消息身份验证码),或者使用包括身份验证的密码模式(见前一点)。
  • DES的有效密钥大小只有56位。这个密钥空间非常小,可以在几个小时内被专门的攻击者强行破解。如果您通过密码生成密钥,这将变得更快。此外,DES的块大小只有64位,这在链接模式中增加了一些弱点。使用现代算法,如AES,其块大小为128位。以及128位的密钥大小(对于最常见的变体,也存在196和256的变体)。
pgvzfuti

pgvzfuti2#

当您为签名密钥输入错误的密码时,这也可能是一个问题。

67up9zun

67up9zun3#

根据您使用的加密算法,您可能需要在加密字节数组之前在末尾添加一些填充字节,以便字节数组的长度是块大小的倍数:
特别是在您的情况下,您选择的填充模式是PKCS5,如下所述:http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_CJ_SYM__PAD.html
(我假设您在尝试加密时遇到了这个问题)
您可以在示例化Cipher对象时选择填充架构。支持的值取决于您使用的安全提供程序。
顺便说一下,你确定要使用对称加密机制来加密密码吗?单向哈希不是更好吗?如果你真的需要能够解密密码,DES是一个相当弱的解决方案,如果你需要使用对称算法,你可能会对使用更强的东西(如AES)感兴趣。

djp7away

djp7away4#

我遇到这个问题是由于操作系统的原因,简单到不同的平台关于JRE的实现。

new SecureRandom(key.getBytes())

将在Windows中获得相同的值,而在Linux中则不同。因此在Linux中需要更改为

SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
kgen.init(128, secureRandom);

“SHA1PRNG”是使用的算法,您可以参考这里了解有关算法的更多信息。

xn1cxnb4

xn1cxnb45#

javax.crypto.BadPaddingException:给定的最后一个块未正确填充。

如果在解密过程中使用了错误密钥,则可能出现此类问题。
对于我自己来说,当我使用错误的密钥进行解密时会发生这种情况。它总是大小写敏感的。所以请确保您在加密时使用了相同的密钥...:)

ukqbszuj

ukqbszuj6#

如果你确定所有的配置都是正确的,那么你可能会因为发送一个空值作为一个加密的解密或者一个平面文本来加密而得到这个异常。

py49o6xq

py49o6xq7#

如果在生成密钥库时收到此警告

Warning:  Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified -keypass value.
Generating 3,072 bit RSA key pair and self-signed certificate (SHA384withRSA) with a validity of 90 days

你可能需要删除应用程序属性文件中的属性secret
光这些属性就够了

encrypt:
  keyStore:
    location: <yourFileLocation>
    password: <yourPassword>
    alias: <yourAlias>

对我很有效。

相关问题