给定最终块未正确填充

发布于 2025-02-07 11:23:03 字数 3218 浏览 1 评论 0 原文

我正在尝试实现基于密码的加密算法,但是我得到了这个例外:

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;
    }
}

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.
        }
    }
}

I am trying to implement password based encryption algorithm, but I get this exception:

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

What might be the problem?

Here is my code:

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 Test)

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.
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

橘和柠 2025-02-14 11:23:04

javax.crypto.badpaddingexception:
给定最终块没有正确的填充。

如果在解密过程中使用了不良钥匙,可能会出现此类问题。

对于我自己来说,当我使用错误的键进行解密时,就会发生这种情况。
这总是敏感的。因此,请确保在加密时使用时使用了相同的密钥....

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

Such issues can arise if a bad key is used during decryption.

For myself this happens when I used wrong key for decrypting.
It is always case sensitive.So make sure you have used the same key when you used when encrypting.... :)

滿滿的愛 2025-02-14 11:23:04

如果您确定所有配置都是正确的,那么由于将null值作为ciphered发送到降低或平面文本以供ENRIPTION,因此可能会得到此例外。

If you sure all configurations are right, so that may you get this exception due to sending a null value as a ciphered to decript or a plane text to encript.

冰雪梦之恋 2025-02-14 11:23:03

如果您尝试使用错误的键解密PKCS5填充数据,然后将其取消(由密码类自动完成),您很可能会获得BadPaddingException(可能小于255/256,约为99.61% ),因为填充物具有特殊的结构,该结构在UNDAD期间经过验证,很少有钥匙会产生有效的填充。

因此,如果您获得此例外,请捕获它并将其视为“错误的密钥”。

当您提供错误的密码时,这也会发生,然后将其用于从密钥库中获取密钥,或使用密钥生成功能将其转换为键。

当然,如果您的数据在运输中损坏,也可能发生不良填充。

也就是说,有关您的方案有一些安全性评论:

  • 对于基于密码的加密,您应该使用SecretKeyFactory和Pbekeyspec,而不是与KeyGenerator一起使用SecureRandom。原因是Secuerandom在每个Java实现中可能是不同的算法,从而为您提供了不同的键。 SecretKeyFactory以定义的方式执行密钥推导(以及一种被认为是安全的方式,如果选择正确的算法)。

  • 不要使用欧洲央行模式。它独立加密每个块,这意味着相同的纯文本块也给出了始终相同的密文块。

    最好使用安全操作模式柜台)。另外,使用一个模式,该模式还包括身份验证,例如GCM(Galois-Counter模式)或CCM(使用CBC-MAC计数器),请参见下一点。

  • 您通常不需要机密性,而不需要身份验证,这确保消息不会被篡改。 (这还可以防止对密码上选择的ciphertext攻击,即有助于机密性。)因此,在您的消息中添加Mac(消息身份验证代码),或使用包含身份验证的密码(请参阅上点)。

    >

  • des的有效密钥大小仅为56位。这个关键空间很小,专门的攻击者可能会在几个小时内蛮力。如果您通过密码生成密钥,这将变得更快。
    另外,DES的块大小仅为64位,这在链接模式中增加了更多的弱点。
    代替使用现代算法,例如AES,其块大小为128位,关键大小为128位(对于最常见的变体,也存在196和256的变体)。

If you try to decrypt PKCS5-padded data with the wrong key, and then unpad it (which is done by the Cipher class automatically), you most likely will get the BadPaddingException (with probably of slightly less than 255/256, around 99.61%), because the padding has a special structure which is validated during unpad and very few keys would produce a valid padding.

So, if you get this exception, catch it and treat it as "wrong key".

This also can happen when you provide a wrong password, which then is used to get the key from a keystore, or which is converted into a key using a key generation function.

Of course, bad padding can also happen if your data is corrupted in transport.

That said, there are some security remarks about your scheme:

  • For password-based encryption, you should use a SecretKeyFactory and PBEKeySpec instead of using a SecureRandom with KeyGenerator. The reason is that the SecureRandom could be a different algorithm on each Java implementation, giving you a different key. The SecretKeyFactory does the key derivation in a defined manner (and a manner which is deemed secure, if you select the right algorithm).

  • Don't use ECB-mode. It encrypts each block independently, which means that identical plain text blocks also give always identical ciphertext blocks.

    Preferably use a secure mode of operation, like CBC (Cipher block chaining) or CTR (Counter). Alternatively, use a mode which also includes authentication, like GCM (Galois-Counter mode) or CCM (Counter with CBC-MAC), see next point.

  • You normally don't want only confidentiality, but also authentication, which makes sure the message is not tampered with. (This also prevents chosen-ciphertext attacks on your cipher, i.e. helps for confidentiality.) So, add a MAC (message authentication code) to your message, or use a cipher mode which includes authentication (see previous point).

  • DES has an effective key size of only 56 bits. This key space is quite small, it can be brute-forced in some hours by a dedicated attacker. If you generate your key by a password, this will get even faster.
    Also, DES has a block size of only 64 bits, which adds some more weaknesses in chaining modes.
    Use a modern algorithm like AES instead, which has a block size of 128 bits, and a key size of 128 bits (for the most common variant, variants for 196 and 256 also exist).

唐婉 2025-02-14 11:23:03

当您输入符号键的错误密码时,这也可能是一个问题。

This can also be a issue when you enter wrong password for your sign key.

怪我鬧 2025-02-14 11:23:03

根据您正在使用的密码学算法的不同,您可能必须在加密字节数组之前在末尾添加一些填充字节,以使字节数组的长度是块大小的多个:

特别是在您的情况下,您选择的填充模式是PKCS5在这里描述:

(我假设您在尝试加密时会出现问题)

/documentation/cryptoj35html/doc/dev_guide/group_< em> cj _sym__pad.html 。支持的值取决于您正在使用的安全提供商。

顺便说一下,您确定要使用对称加密机制加密密码吗?不会是一种更好的哈希吗?如果您真的需要解密密码,那么DES是一个很弱的解决方案,如果您需要使用对称算法,您可能有兴趣使用更强大的AES。

depending on the cryptography algorithm you are using, you may have to add some padding bytes at the end before encrypting a byte array so that the length of the byte array is multiple of the block size:

Specifically in your case the padding schema you chose is PKCS5 which is described here:
http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_CJ_SYM__PAD.html

(I assume you have the issue when you try to encrypt)

You can choose your padding schema when you instantiate the Cipher object. Supported values depend on the security provider you are using.

By the way are you sure you want to use a symmetric encryption mechanism to encrypt passwords? Wouldn't be a one way hash better? If you really need to be able to decrypt passwords, DES is quite a weak solution, you may be interested in using something stronger like AES if you need to stay with a symmetric algorithm.

吾家有女初长成 2025-02-14 11:23:03

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

new SecureRandom(key.getBytes())

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

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

“ sha1prng”是使用的算法,您可以参考在此有关算法的更多信息。

I met this issue due to operation system, simple to different platform about JRE implementation.

new SecureRandom(key.getBytes())

will get the same value in Windows, while it's different in Linux. So in Linux need to be changed to

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

"SHA1PRNG" is the algorithm used, you can refer here for more info about algorithms.

烟─花易冷 2025-02-14 11:23:03

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

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

则可能需要在应用程序属性文件中删除属性秘密

只是这些属性足够

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

对我有用。

If you get this warning when generating a keystore

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

you may need to remove the property secret in your application properties file.

Just these properties are enough

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

It works for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文