java javax.crypto.IllegalBlockSizeException:在 tcp 中使用填充密码解密时,输入长度必须是 16 的倍数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39498722/
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
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher in tcp
提问by Kiara
I am using this AES Encrption and decryption method for encrypting my data. There is no problem with udp but when i use tcp i get this error "javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher"
我正在使用此 AES Encrption 和解密方法来加密我的数据。udp 没有问题,但是当我使用 tcp 时,我收到此错误“javax.crypto.IllegalBlockSizeException:使用填充密码解密时,输入长度必须是 16 的倍数”
AES Encryption/Decryption Code:
AES 加密/解密代码:
public class AESEncDec {
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);
System.err.println("encVal: "+encryptedValue.length());
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);
//byte[] decValue = c.doFinal(encryptedData.getBytes());
String decryptedValue = new String(decValue);
System.err.println("decVal: "+decryptedValue.length());
return decryptedValue;
}
private static Key generateKey() throws Exception {
Key key = new SecretKeySpec(keyValue, ALGO);
return key;
}
}
TCP SERVER CODE:
TCP 服务器代码:
class TCPServer
{
public static void main(String argv[]) throws Exception
{
AESEncDec edData= new AESEncDec();
// AES edData= new AES();
String msg="Message_";
String clientSentence="";
String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(6789);
Socket connectionSocket = welcomeSocket.accept();
for (int i = 0; i < 10; i++)
{
clientSentence=edData.encrypt(msg+i)+"\n";
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
outToClient.writeBytes(clientSentence);
Thread.sleep(100);
}
}
}
TCP CLIENT CODE:
TCP客户端代码:
class TCPClient {
public static void main(String argv[]) throws Exception {
AESEncDec edData= new AESEncDec();
String modifiedSentence;
String DecData="";
Socket clientSocket = new Socket("localhost", 6789);
while(true){
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
modifiedSentence = inFromServer.readLine();
DecData=edData.decrypt(modifiedSentence);
System.out.println("FROM SERVER: " + DecData);
}
//clientSocket.close();
}
}
For the Same code when the message is small it gets decrypted correctly. but when the message is long i get the illegalBlocksize Exception. I tried to use AES/CBC/PKCS5Padding so that the message gets padded and the blocksize comes in a multiple of 16. But still i get either the same error or BadPaddingException. if am specifying PKCS5Padding as the padding technique then the message should be padded correctly and shouldn't be giving this error. why isn't that working. What can i do to decrypt the data correctly. please help..
对于相同的代码,当消息很小时,它会被正确解密。但是当消息很长时,我收到了非法块大小异常。我尝试使用 AES/CBC/PKCS5Padding 以便填充消息并且块大小为 16 的倍数。但我仍然遇到相同的错误或 BadPaddingException。如果我将 PKCS5Padding 指定为填充技术,则应正确填充消息,并且不应出现此错误。为什么这不起作用。我该怎么做才能正确解密数据。请帮忙..
回答by blackpen
The problem is that TCP gives you a stream. If you read a chunk and try to decrypt it, it may fail, because the size of the chunk may not be in multiples of 16 (or 128). AES works on 16 byte or 128 byte chunks of data. So, you may have to wait for a while till you gather that much amount of data before you decrypt.
问题是 TCP 给你一个流。如果您读取一个块并尝试对其进行解密,它可能会失败,因为该块的大小可能不是 16(或 128)的倍数。AES 处理 16 字节或 128 字节的数据块。因此,在解密之前,您可能需要等待一段时间,直到收集到如此多的数据。
Since UDP is message oriented, it doesn't face such problem.
由于 UDP 是面向消息的,所以它不会面临这样的问题。
@Kiara, Please use the Java8's built-in encoding and see how it works out. Refer herefor documentation. Example:
@Kiara,请使用 Java8 的内置编码,看看它是如何工作的。有关文档,请参阅此处。例子:
String asB64 = Base64.getEncoder().encodeToString(data.getBytes("utf-8"));
That was tested to work good with messages of lengths as much as 7 megabytes. It didn't introduce newlines into the encoded messages.
经过测试,它可以很好地处理长达 7 兆字节的消息。它没有在编码的消息中引入换行符。
回答by Artjom B.
AES is a block cipher and can only encrypt exactly 16 bytes to 16 bytes. If you want to encrypt arbitrary inputs, you need a mode of operation and a padding scheme. The default mode is usually ECB and the default padding scheme is PKCS#5 padding (actually PKCS#7 padding).
AES 是一种分组密码,只能加密 16 字节到 16 字节。如果要加密任意输入,则需要一种操作模式和填充方案。默认模式通常是ECB,默认填充方案是PKCS#5 padding(实际上是PKCS#7 padding)。
That means that a single encryption must also be decrypted in exactly the same way, because the padding must be removed during decryption. It is important that multiple ciphertexts don't overlap and are not broken up (chunked / wrapped).
That is what your newline characters are supposed to do during encryption of multiple messages, because you're reading the ciphertexts linewise at the receiving side.
这意味着单个加密也必须以完全相同的方式解密,因为在解密期间必须删除填充。重要的是,多个密文不重叠且不被分解(分块/包裹)。
这就是您的换行符在加密多条消息期间应该做的事情,因为您正在接收方逐行读取密文。
The problem is that the default sun.misc.BASE64Encoder#encode
method actually introduces newlines on its own. Every 76 characters there is a newline inserted, but the receiver cannot know which newline actually separates the message ciphertexts and which newline separates the wrapped lines in a single message ciphertext.
问题是默认sun.misc.BASE64Encoder#encode
方法实际上自己引入了换行符。每 76 个字符插入一个换行符,但接收者无法知道哪个换行符实际分隔消息密文,哪个换行符分隔单个消息密文中的换行符。
The easiest fix would be to remove the newlines after Base64-encoding:
最简单的解决方法是在 Base64 编码后删除换行符:
String encryptedValue = new BASE64Encoder().encode(encVal).replaceAll("\s", "");
This replaces all whitespace in a single message ciphertext.
这将替换单个消息密文中的所有空格。
Since you're using the private sun.misc.*
classes, it is better to move to another Base64 implementations, because those classes don't have to be available in every JVM. You should use some known library like Apache's commons-codec which provides an implementationwhere you can disable wrapping/chunking:
由于您使用的是私有sun.misc.*
类,因此最好转移到另一个 Base64 实现,因为这些类不必在每个 JVM 中都可用。你应该使用Apache的公地编解码器提供了一些已知的库实现,你可以禁用包装/分块:
String encryptedValue = Base64.encodeBase64(encVal, false);
which is equivalent to
这相当于
String encryptedValue = Base64.encodeBase64(encVal);