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

我在java类中遇到解密错误:

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

我该怎么做才能解决这个问题?

更新:

我忘了提到它工作一次,第二次我试图再次执行它时抛出上述错误。

 package com.tb.module.service; import java.security.Key; import java.security.spec.InvalidKeySpecException; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import sun.misc.*; /** * This class is used for encrypt and decrypt the password field. * */ public class PswdEnc { private static final String ALGO = "AES"; private static final byte[] keyValue = new byte[] { 'T', 'h', 'e', 'B', 'e', 's', 't','S', 'e', 'c', 'r','e', 't', 'K', 'e', 'y' }; public static String encrypt(String Data) throws Exception { Key key = generateKey(); Cipher c = Cipher.getInstance(ALGO); c.init(Cipher.ENCRYPT_MODE, key); byte[] encVal = c.doFinal(Data.getBytes()); String encryptedValue = new BASE64Encoder().encode(encVal); return encryptedValue; } public static String decrypt(String encryptedData) throws Exception { Key key = generateKey(); Cipher c = Cipher.getInstance(ALGO); c.init(Cipher.DECRYPT_MODE, key); byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData); byte[] decValue = c.doFinal(decordedValue); String decryptedValue = new String(decValue); return decryptedValue; } private static Key generateKey() throws Exception { Key key = new SecretKeySpec(keyValue, ALGO); return key; } } 

您使用的算法“AES”是“AES / ECB / NoPadding”的简写。 这意味着您使用的是具有128位密钥大小和块大小的AES算法 ,具有ECB 操作模式且无填充 。

换句话说:您只能以128位或16字节的块加密数据。 这就是你得到IllegalBlockSizeExceptionexception的原因。

如果要加密大小不是16字节的倍数的数据,则要么必须使用某种填充,要么使用密码流。 例如,通过指定“AES / CBC / NoPadding”作为算法,或者通过指定“AES / ECB / PKCS5”指定PKCS5填充,您可以使用CBC模式 (一种有效地将分组密码转换为流密码的操作模式),它将以非常特定的格式自动在数据末尾添加一些字节,以使密文的大小为16字节的倍数,并且解密算法将理解它必须忽略某些数据。

无论如何,我强烈建议您立即停止正在做的事情,并研究一些关于密码学的介绍性材料。 例如,检查Coursera上的Crypto I。 你应该很清楚选择一种或另一种模式的含义,它们的优点是什么,最重要的是它们的弱点。 没有这方面的知识,很容易构建非常容易破解的系统。


更新:根据您对问题的评论,在将密码存储到数据库时不要加密密码 ! 你永远不应该这样做。 您必须HASH密码,正确腌制,这与加密完全不同。 真的,拜托,不要做你想做的事情……通过加密密码,可以解密它们。 这意味着您作为数据库管理员和谁知道密钥,您将能够读取存储在数据库中的每个密码。 要么你知道这一点,要么做得非常非常糟糕,或者你不知道这一点,应该感到震惊并制止它。

一些评论:

import sun.misc.*; 不要这样做。 它是非标准的,并不保证在实现之间是相同的。 还有其他可用Base64转换的库。

byte[] encVal = c.doFinal(Data.getBytes()); 您在这里依赖于默认字符编码。 始终指定您正在使用的字符编码: byte[] encVal = c.doFinal(Data.getBytes("UTF-8")); 不同地方的默认值可能不同。

正如@thegrinner指出的那样,您需要显式检查字节数组的长度。 如果存在差异,则逐字节比较它们以查看差异在哪里蔓延。

要解决问题,你需要在你的代码中做一些改变,只需要让你的类的返回类型为加密()类型为byte []数组,并在你的类的decrypt()API中传递byte []数组,这样你可以解决输入长度为16的exception倍数。

请参考下面的工作代码:

 public static byte[] encrypt(String value) { byte[] encrypted = null; try { byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'}; Key skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); byte[] iv = new byte[cipher.getBlockSize()]; IvParameterSpec ivParams = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, skeySpec,ivParams); encrypted = cipher.doFinal(value.getBytes()); System.out.println("encrypted string:" + encrypted.length); } catch (Exception ex) { ex.printStackTrace(); } return encrypted; } public static byte[] decrypt(byte[] encrypted) { byte[] original = null; Cipher cipher = null; try { byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'}; Key key = new SecretKeySpec(raw, "AES"); cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //the block size (in bytes), or 0 if the underlying algorithm is not a block cipher byte[] ivByte = new byte[cipher.getBlockSize()]; //This class specifies an initialization vector (IV). Examples which use //IVs are ciphers in feedback mode, eg, DES in CBC mode and RSA ciphers with OAEP encoding operation. IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte); cipher.init(Cipher.DECRYPT_MODE, key, ivParamsSpec); original= cipher.doFinal(encrypted); } catch (Exception ex) { ex.printStackTrace(); } return original; } 

那是因为

您只能以128位或16字节的块加密数据。 这就是你得到IllegalBlockSizeExceptionexception的原因。 一种方法是直接将数据加密到String中。

看看这个。 尝试,你将能够解决这个问题

 public static String decrypt(String encryptedData) throws Exception { Key key = generateKey(); Cipher c = Cipher.getInstance(ALGO); c.init(Cipher.DECRYPT_MODE, key); String decordedValue = new BASE64Decoder().decodeBuffer(encryptedData).toString().trim(); System.out.println("This is Data to be Decrypted" + decordedValue); return decordedValue; } 

希望这会有所帮助。