获取javax.crypto.IllegalBlockSizeException:使用填充密码解密时,输入长度必须是16的倍数?

使用tomcat,我有两个web应用程序,即app1和app2。 我将app1以加密forms(使用下面的代码)发送到app2。 然后在app2我解密了这个加密的url。 但是我在decryp方法的第50行遇到了exception。

 "Getting javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher" 

虽然我尝试在app1解密(使用相同的代码)加密的URL时进行调试,但它工作正常。 但无法弄清楚在app2引起此exception的原因是什么?

这是代码

  import java.security.Key; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; public class AESEncryptionDecryptionTest { private static final String ALGORITHM = "AES"; private static final String myEncryptionKey = "ThisIsFoundation"; private static final String UNICODE_FORMAT = "UTF8"; public static String encrypt(String valueToEnc) throws Exception { Key key = generateKey(); Cipher c = Cipher.getInstance(ALGORITHM); c.init(Cipher.ENCRYPT_MODE, key); byte[] encValue = c.doFinal(valueToEnc.getBytes()); String encryptedValue = new BASE64Encoder().encode(encValue); return encryptedValue; } public static String decrypt(String encryptedValue) throws Exception { Key key = generateKey(); Cipher c = Cipher.getInstance(ALGORITHM); c.init(Cipher.DECRYPT_MODE, key); byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedValue); byte[] decValue = c.doFinal(decordedValue);//////////LINE 50 String decryptedValue = new String(decValue); return decryptedValue; } private static Key generateKey() throws Exception { byte[] keyAsBytes; keyAsBytes = myEncryptionKey.getBytes(UNICODE_FORMAT); Key key = new SecretKeySpec(keyAsBytes, ALGORITHM); return key; } public static void main(String[] args) throws Exception { String value = "password1"; String valueEnc = AESEncryptionDecryptionTest.encrypt(value); String valueDec = AESEncryptionDecryptionTest.decrypt(valueEnc); System.out.println("Plain Text : " + value); System.out.println("Encrypted : " + valueEnc); System.out.println("Decrypted : " + valueDec); } } 

适用于我的机器。 如果在将字节转换为字符串的每个实例中使用`UNICODE_FORMAT’,反之亦然,这会有帮助吗? 这条线可能是一个问题:

 byte[] encValue = c.doFinal(valueToEnc.getBytes()); 

应该

 byte[] encValue = c.doFinal(valueToEnc.getBytes(UNICODE_FORMAT)); 

无论如何,如果您使用“AES”作为算法并使用JCE,则实际使用的算法将是“AES / ECB / PKCS5Padding”。 除非您100%确定自己在做什么,否则不应该使用ECB。 我建议总是明确指定算法,以避免这种混淆。 “AES / CBC / PKCS5Padding”将是一个不错的选择。 但请注意,使用任何合理的算法,您还必须提供和管理IV。

在加密密码的情况下使用ECB密码甚至不太理想,如果我正确地解释您的示例,这就是您在加密时所做的事情。 您应该使用PKCS#5中为此目的指定的基于密码的加密,在Java中,这是在SecretKeyFactory中为您提供的。 确保使用“PBKDF2WithHmacSHA1”具有足够高的迭代次数(任何范围从~5-20 000,取决于您的目标机器),以使用密码从它们派生对称密钥。

如果它实际上是密码存储而不是您想要实现的密码加密,则可以使用相同的技术。

我建议使用字节[]本身而不是使用字符串。 我猜测将一些字节转换为String时会修改一些字节。 以下代码适用于我 –

 public static final String ENC_KEY = "abcdefghijklmnop"; public static final String DATA = "Hello World"; public static void test(){ try { Cipher c = Cipher.getInstance("AES"); SecretKeySpec secretKeySpec = new SecretKeySpec(ENC_KEY.getBytes("UTF-8"), "AES"); c.init(Cipher.ENCRYPT_MODE, secretKeySpec); byte[] encBytes = c.doFinal(DATA.getBytes("UTF-8")); String encStr = new String(encBytes, "UTF-8"); System.out.println("Encrypted String: " + encStr); c.init(Cipher.DECRYPT_MODE, secretKeySpec); String decStr = new String(c.doFinal(encBytes),"UTF-8"); System.out.println("Decrypted String: " + decStr); } catch (Exception ex) { System.out.println("Error in encrypting data"); ex.printStackTrace(); } } 

但如果你把它改成 –

 public static void test(){ try { Cipher c = Cipher.getInstance("AES"); SecretKeySpec secretKeySpec = new SecretKeySpec(ENC_KEY.getBytes("UTF-8"), "AES"); c.init(Cipher.ENCRYPT_MODE, secretKeySpec); byte[] encBytes = c.doFinal(DATA.getBytes("UTF-8")); String encStr = new String(encBytes, "UTF-8"); System.out.println("Encrypted String: " + encStr); c.init(Cipher.DECRYPT_MODE, secretKeySpec); String decStr = new String(c.doFinal(encStr.getBytes("UTF-8")),"UTF-8"); System.out.println("Decrypted String: " + decStr); } catch (Exception ex) { System.out.println("Error in encrypting data"); ex.printStackTrace(); } } 

你会得到

javax.crypto.IllegalBlockSizeException:当使用填充密码在com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:936)的com.sun.crypto.provider.CipherCore.doFinal解密时,输入长度必须是16的倍数( CipherCore.java:847)com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)at javax.crypto.Cipher.doFinal(Cipher.java:2164)at com.osfg.HelloWorld.test(HelloWorld) .java:38)at com.osfg.HelloWorld.main(HelloWorld.java:22)

注意

 String decStr = new String(c.doFinal(encBytes),"UTF-8"); VRS String decStr = new String(c.doFinal(encStr.getBytes("UTF-8")),"UTF-8"); 

当双重解密一个值时,我看到了这个错误(密码通过就好了,就像你在这里说的那样)。 务必检查您是否多次进行解密。 (我的错误是8的倍数,但我在那里使用了不同的方案……)在我的情况下,我在读取文件时解密,然后在填写字段时再次解密。 (桌面应用)

此错误表示您使用的选择组合仅需要16个字符的源文本。 如果要加密密码,可以将原始密码截断或填充为16-char进行加密,并在解密后进行修剪。 这种方式必须限制实际密码不超过16-char,但您可以使用较长时间使用的密码来混淆那些不知道您密码的人。