java 最后一个块与 CipherInputStream/CipherOutputStream 不完整,即使使用填充 AES/CBC/PKCS5Padding
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12799726/
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
last block incomplete with CipherInputStream/CipherOutputStream, even with padding AES/CBC/PKCS5Padding
提问by Mani
Actually, I searched lot from internet and in stackoverflow too for this,
实际上,我也为此从互联网和 stackoverflow 中搜索了很多,
Initially I don't used padding in my encryption and decryption,
最初我没有在我的加密和解密中使用填充,
But Finally I got solution from here
但最后我从这里得到了解决方案
https://stackoverflow.com/a/10775577/1115788
https://stackoverflow.com/a/10775577/1115788
and I updated my code with padding as AES/CBC/PKCS5Padding and the same error is coming, and last block is not decrypted...
我用 AES/CBC/PKCS5Padding 填充更新了我的代码,同样的错误即将到来,最后一个块没有解密......
I'm working on this for last two day, but no solution found
我最近两天都在研究这个,但没有找到解决方案
my Crypter Code:
我的密码:
package mani.droid.browsedropbox;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Crypter {
Cipher encipher;
Cipher decipher;
CipherInputStream cis;
CipherOutputStream cos;
FileInputStream fis;
byte[] ivbytes = new byte[]{(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', (byte)'o', (byte)'p'};
IvParameterSpec iv = new IvParameterSpec(ivbytes);
public boolean enCrypt(String key, InputStream is, OutputStream os)
{
try {
byte[] encoded = new BigInteger(key, 16).toByteArray();
SecretKey seckey = new SecretKeySpec(encoded, "AES");
encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
encipher.init(Cipher.ENCRYPT_MODE, seckey, iv);
cis = new CipherInputStream(is, encipher);
copyByte(cis, os);
return true;
}
catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
public boolean deCrypt(String key, InputStream is, OutputStream os)
{
try {
byte[] encoded = new BigInteger(key, 16).toByteArray();
SecretKey seckey = new SecretKeySpec(encoded, "AES");
encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
encipher.init(Cipher.DECRYPT_MODE, seckey, iv);
cos = new CipherOutputStream(os, encipher);
copyByte(is, cos);
//cos.close();
return true;
}
catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
public void copyByte(InputStream is, OutputStream os) throws IOException
{
byte[] buf = new byte[8192];
int numbytes;
while((numbytes = is.read(buf)) != -1)
{
os.write(buf, 0, numbytes);
os.flush();
}
os.close();
is.close();
}
}
采纳答案by ph4r05
I've had exactly the same problem. The accepted solution works because you've used cipher mode that does not require padding, but this is not a way crypto-related issues are fixed.
我遇到了完全相同的问题。接受的解决方案有效,因为您使用了不需要填充的密码模式,但这不是解决与加密相关的问题的方法。
According to the CipherOutputStream documentation, you have to call close() method in order to finalize encryption properly (i.e., padding block is added).
根据 CipherOutputStream文档,您必须调用 close() 方法才能正确完成加密(即添加了填充块)。
This method invokes the doFinal method of the encapsulated cipher object, which causes any bytes buffered by the encapsulated cipher to be processed. The result is written out by calling the flush method of this output stream.
This method resets the encapsulated cipher object to its initial state and calls the close method of the underlying output stream.
此方法调用封装密码对象的 doFinal 方法,这会导致处理封装密码缓冲的任何字节。结果通过调用这个输出流的flush方法写出。
此方法将封装的密码对象重置为其初始状态并调用底层输出流的 close 方法。
If you want to preserve OutputStream open even after calling CipherOutputStream.close() method, you can wrap OutputStream to the stream that does not close it. For example:
如果您想在调用 CipherOutputStream.close() 方法后仍保持 OutputStream 打开,您可以将 OutputStream 包装到不关闭它的流中。例如:
public class NotClosingOutputStream extends OutputStream {
private final OutputStream os;
public NotClosingOutputStream(OutputStream os) {
this.os = os;
}
@Override
public void write(int b) throws IOException {
os.write(b);
}
@Override
public void close() throws IOException {
// not closing the stream.
}
@Override
public void flush() throws IOException {
os.flush();
}
@Override
public void write(byte[] buffer, int offset, int count) throws IOException {
os.write(buffer, offset, count);
}
@Override
public void write(byte[] buffer) throws IOException {
os.write(buffer);
}
}
Then you can use:
然后你可以使用:
...
cos = new CipherOutputStream(new NotClosingOutputStream(os), encipher);
copyByte(is, cos);
cos.close();
...
Note the os
stream does not get closed, you need to do it on your own when appropriate.
请注意,os
流不会关闭,您需要在适当的时候自行完成。
回答by Mani
Finally I got answer for my own question, with trial and error
Actually here Conflict is I set Padding in encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
最后我得到了我自己的问题的答案,反复试验实际上在这里冲突是我设置了填充 encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
and Set IV with some values.....,
并用一些值设置 IV.....,
Finally I got Answer only just replaced the Algorithm
最后我得到的答案只是替换了算法
From:
从:
AES/CBC/PKCS7Paddinng
AES/CBC/PKCS7Padding
To:
到:
AES/CFB8/NoPadding
AES/CFB8/NoPadding
and its worked like charm...., So I suggest this answer for others who struggling with this problem, if you solved you problem, mention here for others...
并且它的作用就像魅力......,所以我建议其他在这个问题上苦苦挣扎的人的答案,如果你解决了你的问题,请在这里为其他人提及......
回答by Anthony Hayward
I've seen CipherInputStream fail with padding problems too. This behaviour varied across different versions of the JVM. Eg 7u55 32 bit my code worked fine, 7u55 64 bit same code failed... and I also saw failures on later 32 bit JVMs. Workaround was to use byte array methods and avoid CipherInputStream.
我也看到 CipherInputStream 因填充问题而失败。此行为因 JVM 的不同版本而异。例如,7u55 32 位我的代码运行良好,7u55 64 位相同的代码失败了……我也看到了后来的 32 位 JVM 失败。解决方法是使用字节数组方法并避免 CipherInputStream。
回答by 18446744073709551615
Not sure if this is relevant to OP's problem, but this may help someone.
不确定这是否与 OP 的问题有关,但这可能对某人有所帮助。
When you repeatedlyget that java.io.IOException: last block incomplete in decryption
regardless of what you change,check if you are still using the file from some previous run. If your read/write test code appends to that file, you will always get that exception -- unless you delete the corrupt file that you write to.
当无论您更改什么内容都反复获得java.io.IOException: last block incomplete in decryption
该文件时,请检查您是否仍在使用之前运行的文件。如果您的读/写测试代码附加到该文件,您将始终收到该异常——除非您删除了您写入的损坏文件。
回答by Robert
Using CipherInputStream with padding is possible, switching to NoPadding is a workaround but not a solution.
使用带有填充的 CipherInputStream 是可能的,切换到 NoPadding 是一种解决方法,但不是解决方案。
Padding is applied when CipherInputStream reaches the end of the stream. The important point is that you have to call the read() method of the CipherInputStream at least twice for getting all the data.
当 CipherInputStream 到达流的末尾时应用填充。重要的一点是,您必须至少调用 CipherInputStream 的 read() 方法两次才能获取所有数据。
The following example demonstrates reading a CipherInputStream with padding:
以下示例演示了如何使用填充读取 CipherInputStream:
public static void test() throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom rnd = new SecureRandom();
byte[] keyData = new byte[16];
byte[] iv = new byte[16];
rnd.nextBytes(keyData);
rnd.nextBytes(iv);
SecretKeySpec key = new SecretKeySpec(keyData, "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
CipherOutputStream out = new CipherOutputStream(buffer, cipher);
byte[] plain = "Test1234567890_ABCDEFG".getBytes();
out.write(plain);
out.flush();
out.close();
byte[] encrypted = buffer.toByteArray();
System.out.println("Plaintext length: " + plain.length);
System.out.println("Padding length : " + (cipher.getBlockSize() - (plain.length % cipher.getBlockSize())));
System.out.println("Cipher length : " + encrypted.length);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
CipherInputStream in = new CipherInputStream(new ByteArrayInputStream(encrypted), cipher);
buffer = new ByteArrayOutputStream();
byte[] b = new byte[100];
int read;
while ((read = in.read(b)) >= 0) {
buffer.write(b, 0, read);
}
in.close();
// prints Test1234567890_ABCDEFG
System.out.println(new String(buffer.toByteArray()));
}