java java中AES解密算法的异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3180878/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Exception in AES decryption algorithm in java
提问by sparkle
I got an exception in the following code for AES algorithm in java.
我在 Java 中的 AES 算法的以下代码中遇到了异常。
Code decryptes an encrypted string and returns the original string.
代码解密加密字符串并返回原始字符串。
Plz help me to fix this.
请帮我解决这个问题。
Code:
代码:
public class AES
{
public byte[] encrypted;
public byte[] original;
public String originalString;
public static String asHex (byte buf[])
{
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i; for (i = 0; i < buf.length; i++)
{
if (((int) buf[i] & 0xff) < 0x10) strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public String AESencryptalgo(byte[] text)
{
String newtext="";
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec); encrypted = cipher.doFinal(text);
System.out.println("encrypted string: " + asHex(encrypted));
cipher.init(Cipher.DECRYPT_MODE, skeySpec); original = cipher.doFinal(encrypted);
originalString = new String(original); System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{ }
finally
{
newtext=new String(encrypted);
System.out.println("ENCRYPTED "+newtext);
//AESdecryptalgo(newtext.getBytes());
return newtext;
}
}
public String AESdecryptalgo(byte[] text)
{
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
original = cipher.doFinal(text); //Exception occurs here
originalString = new String(original);
System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{
System.out.println("exception");
}
finally
{
System.out.println("DECRYPTED "+originalString);
return originalString;
}
}
public static void main(String[] args)
{
AES a=new AES();
a.AESencryptalgo("hello".getBytes());
System.out.println();
}}
`
exception:
例外:
javax.crypto.BadPaddingException: Given final block not properly padded at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..) at
javax.crypto.Cipher.doFinal(DashoA13*..)
回答by Adam Paynter
According to Java? Cryptography Architecture (JCA) Reference Guide(emphasis mine):
根据Java?密码体系结构 (JCA) 参考指南(重点是我的):
Cipherobjects are obtained by using one of theCipher getInstance()static factory methods. Here, the algorithm name is slightly different than with other engine classes, in that it specifies not just an algorithm name, but a "transformation". A transformation is a string that describes the operation (or set of operations) to be performed on the given input to produce some output. A transformation always includes the name of a cryptographic algorithm (e.g.,DES), and may be followed by a mode and padding scheme.A transformation is of the form:
- "algorithm/mode/padding" or
- "algorithm"
For example, the following are valid transformations:
"DES/CBC/PKCS5Padding""DES"If just a transformation name is specified, the system will determine if there is an implementation of the requested transformation available in the environment, and if there is more than one, returns there is a preferred one.
If both a transformation name and a package provider are specified, the system will determine if there is an implementation of the requested transformation in the package requested, and throw an exception if there is not.
If no mode or padding is specified, provider-specific default values for the mode and padding scheme are used.For example, the
SunJCEprovider usesECBas the default mode, andPKCS5Paddingas the default padding scheme forDES,DES-EDEandBlowfishciphers. This means that in the case of theSunJCEprovider:Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");and
Cipher c1 = Cipher.getInstance("DES");are equivalent statements.
Using modes such as CFB and OFB, block ciphers can encrypt data in units smaller than the cipher's actual block size. When requesting such a mode, you may optionally specify the number of bits to be processed at a time by appending this number to the mode name as shown in the "DES/CFB8/NoPadding" and "DES/OFB32/PKCS5Padding" transformations. If no such number is specified, a provider-specific default is used. (For example, the
SunJCEprovider uses a default of 64 bits for DES.) Thus, block ciphers can be turned into byte-oriented stream ciphers by using an 8 bit mode such as CFB8 or OFB8.Appendix Aof this document contains a list of standard names that can be used to specify the algorithm name, mode, and padding scheme components of a transformation.
The objects returned by factory methods are uninitialized, and must be initialized before they become usable.
Cipher对象是通过使用Cipher getInstance()静态工厂方法之一获得的。在这里,算法名称与其他引擎类略有不同,因为它不仅指定算法名称,还指定“转换”。转换是一个字符串,它描述了要对给定输入执行的操作(或操作集)以产生一些输出。转换总是包括密码算法的名称(例如,DES),并且可能跟随着模式和填充方案。转换的形式为:
- “算法/模式/填充”或
- 《算法》
例如,以下是有效的转换:
"DES/CBC/PKCS5Padding""DES"如果只指定了一个转换名称,系统将确定环境中是否有可用的请求转换的实现,如果有多个,则返回一个首选的。
如果同时指定了转换名称和包提供者,则系统将确定所请求的包中是否存在所请求转换的实现,如果没有则抛出异常。
如果未指定模式或填充,则使用模式和填充方案的特定于提供者的默认值。例如,
SunJCE提供程序ECB用作默认模式,并PKCS5Padding用作DES,DES-EDE和Blowfish密码的默认填充方案。这意味着在SunJCE提供者的情况下:Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");和
Cipher c1 = Cipher.getInstance("DES");是等价的语句。
使用 CFB 和 OFB 等模式,块密码可以以小于密码实际块大小的单位加密数据。当请求这种模式时,您可以选择指定一次要处理的位数,方法是将此数字附加到模式名称中,如“ DES/CFB8/NoPadding”和“ DES/OFB32/PKCS5Padding”转换中所示。如果未指定此类数字,则使用特定于提供者的默认值。(例如,
SunJCE提供商使用 DES 的默认值 64 位。)因此,可以通过使用 8 位模式(例如 CFB8 或 OFB8)将分组密码转换为面向字节的流密码。本文档的附录 A包含标准名称列表,可用于指定转换的算法名称、模式和填充方案组件。
工厂方法返回的对象是未初始化的,必须在它们可用之前进行初始化。
Because your code does not specify mode or padding, provider-specific default values are being used. It appears that your provider is SunJCEand that it's default padding is probably "NoPadding". With this padding, you are responsible for ensuring that the size of the byte array being encrypted is a multiple of the number of bytes in the secret key. You can make you're life easier by specifying the mode and padding in your transformation:
由于您的代码未指定模式或填充,因此正在使用特定于提供程序的默认值。看来您的提供者是SunJCE并且它的默认填充可能是"NoPadding". 使用此填充,您负责确保被加密的字节数组的大小是密钥中字节数的倍数。您可以通过在转换中指定模式和填充来让您的生活更轻松:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
WARNING: You should not use ECB mode in real code. Try CBC instead.
警告:您不应在实际代码中使用 ECB 模式。试试 CBC。
Update:I didn't think it was fair to recommend CBC mode without offering a little sample of how it works:
更新:我认为在不提供有关其工作原理的小样本的情况下推荐 CBC 模式是不公平的:
public static void main(String... args) throws Exception {
byte[] data = "hello".getBytes();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// By initializing the cipher in CBC mode, an "initialization vector" has been randomly
// generated. This initialization vector will be necessary to decrypt the encrypted data.
// It is safe to store the initialization vector in plain text for later use. You can obtain
// it's bytes by calling iv.getIV().
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
byte[] encryptedData = cipher.doFinal(data);
// When decrypting the encrypted data, you must provide the initialization vector used
// during the encryption phase.
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedData = cipher.doFinal(encryptedData);
if (!Arrays.equals(data, decryptedData)) {
throw new Exception("Data was not decrypted successfully");
}
}
回答by scigor
Well if this is the error Input length must be multiple of 16 when decrypting with padded cipher. Than the answear is obvious, the length of your buffer must be a multiple of 16. Have you checked the length of buf[]?
好吧,如果这是错误,当使用填充密码解密时,输入长度必须是 16 的倍数。比 answear 明显,你的缓冲区的长度必须是 16 的倍数。你有没有检查过 buf[] 的长度?
回答by President James K. Polk
Your code manages to get almost everything wrong. Just for a start, your mistakes include:
你的代码几乎把所有事情都弄错了。首先,您的错误包括:
- generating a new random symmetric key before encryption and decryption. You have to use the same key to decrypt that was used to encrypt.
- Using String as a container for binary data. cipher output is cannot be reliably made into a String unless you use an encoding, such as base64.
- Your exception handling is incorrect.
- 在加密和解密之前生成一个新的随机对称密钥。您必须使用与加密时相同的密钥来解密。
- 使用 String 作为二进制数据的容器。除非您使用诸如 base64 之类的编码,否则无法将 cipher 输出可靠地转换为 String。
- 您的异常处理不正确。
Furthermore, your code runs without exceptions for me.
此外,您的代码对我来说无一例外地运行。

