这个问题与以下内容完全相同:
当我从CipherInputStream读取对象时,为什么会出现无效的流标头错误? (编辑:可能是由于重用了Cipher对象?,下面包含的新版本的示例代码已经解决了这个问题 . )
我的程序试图从ObjectInputStream中读取,该ObjectInputStream使用CipherInputStream作为源(源自文件),即:
文件 - > DeCipher Stream - > DeSerialize - > Object
运行时错误是:
java.io.StreamCorruptedException: invalid stream header: 73720019
下面列出的示例程序中的write方法使用普通的FileOutputStream and 将文件写为CipherOutputStream,因此我们要检查2个文件 . read方法同样尝试读取这两个文件 . 普通文件的写入和读取没有问题,但加密文件会引发异常 . 如果查看普通文件的前8个字节,可以看到它们是:
0000000 254 355 \0 005 s r \0 031 j a v a x . c r
ac ed 00 05 73 72 00 19 6a 61 76 61 78 2e 63 72
"ac ed 00 05"对应于ObjectStream Magic和Version(即 Headers ):https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html
但是Exception报告的'corrupt' Headers 对应于 Headers 后面的四个字节:73 72 00 19!
在我看来,CipherInputStream正在正确解密流但消耗或跳过标头本身并从字节5开始传递数据 .
这是糟糕的输出:
run:
Read Object (plain): Test
Exception in readTest:
java.io.StreamCorruptedException: invalid stream header: 73720019
BUILD SUCCESSFUL (total time: 0 seconds)
这是代码:
package encryptedobjectstreamexample;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import static javax.crypto.Cipher.*;
import javax.crypto.spec.*;
public class EncryptedObjectStreamExample {
static boolean cipherOn = true;
static final String fn = "C:/Temp/object.";
static final byte[] keyBytes = "MySecretPass1234".getBytes();
static final byte[] iv = "initialialvector".getBytes();
static String testObject = "Test";
Cipher c;
AlgorithmParameters ap;
IvParameterSpec ivp;
Key k;
public EncryptedObjectStreamExample() {
try {
c = Cipher.getInstance("AES/CBC/PKCS5Padding");
ap = AlgorithmParameters.getInstance("AES");
ivp = new IvParameterSpec(iv);
ap.init(ivp);
k = new SecretKeySpec(keyBytes, "AES");
} catch (Exception ex) {
System.err.println("Failed Constructor:\n" + ex);
System.exit(1);
}
}
public void writeTest() {
// Object -> Serialize -> Cipher Stream -> File
try {
c.init(ENCRYPT_MODE, k, ap);
OutputStream ostrp = new FileOutputStream(fn+"p");
OutputStream ostrx = new CipherOutputStream(new FileOutputStream(fn+"x"),c);
try (ObjectOutputStream oos = new ObjectOutputStream(ostrp)) {
oos.writeObject(new SealedObject(testObject, c));
}
try (ObjectOutputStream oos = new ObjectOutputStream(ostrx)) {
oos.writeObject(new SealedObject(testObject, c));
}
} catch (Exception e) {
System.err.println("Exception in writeTest: \n" + e);
}
}
private void readTest() {
// File -> DeCipher Stream -> DeSerialize -> Object
try {
c.init(DECRYPT_MODE, k, ap);
InputStream istrp = new FileInputStream("C:/Temp/object.p");
InputStream istrx = new CipherInputStream(new FileInputStream("C:/Temp/object.x"),c);
try (ObjectInputStream ois = new ObjectInputStream(istrp)) {
String result = (String) (((SealedObject) ois.readObject()).getObject(c));
System.out.println("Read Object (plain): " + result);
}
try (ObjectInputStream ois = new ObjectInputStream(istrx)) {
String result = (String) (((SealedObject) ois.readObject()).getObject(c));
System.out.println("Read Object (encrypt): " + result);
}
} catch (Exception e) {
System.err.println("Exception in readTest: \n" + e);
}
}
public static void main(String[] args) {
EncryptedObjectStreamExample eos = new EncryptedObjectStreamExample();
eos.writeTest();
eos.readTest();
}
}
编辑:我已经稍微重新编写了代码,以便为流和Seal操作使用不同的Cipher对象:x.sealdob和x.iostream
此修改后的代码现在可以正确运行,两个文件都可以无错误地写入和读回:
这是新的(好的)输出:
run:
Read Object (plain): Test
Read Object (encrypt): Test
BUILD SUCCESSFUL (total time: 0 seconds)
这是更新的代码(有效):
package encryptedobjectstreamexample;
import java.io.*;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.*;
import static javax.crypto.Cipher.*;
import javax.crypto.spec.*;
public class EncryptedObjectStreamCipherX {
static boolean cipherOn = true;
static final String FN = "C:/Temp/object.";
static final byte[] keyBytes = "MySecretPass1234".getBytes();
static final byte[] IV = "initialialvector".getBytes();
static String testObject = "Test";
Xipher x;
Key k;
private class Xipher {
AlgorithmParameters ap;
Cipher iostream, sealedob;
static final String ALG = "AES";
Xipher() {
try {
iostream = Cipher.getInstance(ALG + "/CBC/PKCS5Padding");
sealedob = Cipher.getInstance(ALG + "/CBC/PKCS5Padding");
ap = AlgorithmParameters.getInstance(ALG);
ap.init(new IvParameterSpec(IV));
k = new SecretKeySpec(keyBytes, "AES");
} catch (NoSuchPaddingException | InvalidParameterSpecException | NoSuchAlgorithmException ex) {
Logger.getLogger(EncryptedObjectStreamCipherX.class.getName()).log(Level.SEVERE, null, ex);
}
}
void encryptMode() {
try {
iostream.init(ENCRYPT_MODE, new SecretKeySpec(keyBytes, ALG), ap);
sealedob.init(ENCRYPT_MODE, new SecretKeySpec(keyBytes, ALG), ap);
} catch (InvalidKeyException | InvalidAlgorithmParameterException ex) {
Logger.getLogger(EncryptedObjectStreamCipherX.class.getName()).log(Level.SEVERE, null, ex);
}
}
void decryptMode() {
try {
iostream.init(DECRYPT_MODE, new SecretKeySpec(keyBytes, ALG), ap);
sealedob.init(DECRYPT_MODE, new SecretKeySpec(keyBytes, ALG), ap);
} catch (InvalidKeyException | InvalidAlgorithmParameterException ex) {
Logger.getLogger(EncryptedObjectStreamCipherX.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public EncryptedObjectStreamCipherX() {
try {
x = new Xipher();
} catch (Exception ex) {
System.err.println("Failed Constructor:\n" + ex);
System.exit(1);
}
}
public void writeTest() {
// Object -> Serialize -> Cipher Stream -> File
try {
x.encryptMode();
OutputStream ostrp = new FileOutputStream(FN + "p");
OutputStream ostrx = new CipherOutputStream(new FileOutputStream(FN + "x"), x.iostream);
try (ObjectOutputStream oos = new ObjectOutputStream(ostrp)) {
oos.writeObject(new SealedObject(testObject, x.sealedob));
}
try (ObjectOutputStream oos = new ObjectOutputStream(ostrx)) {
oos.writeObject(new SealedObject(testObject, x.sealedob));
}
} catch (Exception e) {
System.err.println("Exception in writeTest: \n" + e);
}
}
private void readTest() {
// File -> DeCipher Stream -> DeSerialize -> Object
try {
x.decryptMode();
InputStream istrp = new FileInputStream("C:/Temp/object.p");
InputStream istrx = new CipherInputStream(new FileInputStream("C:/Temp/object.x"), x.iostream);
try (ObjectInputStream ois = new ObjectInputStream(istrp)) {
String result = (String) (((SealedObject) ois.readObject()).getObject(x.sealedob));
System.out.println("Read Object (plain): " + result);
}
try (ObjectInputStream ois = new ObjectInputStream(istrx)) {
String result = (String) (((SealedObject) ois.readObject()).getObject(x.sealedob));
System.out.println("Read Object (encrypt): " + result);
}
} catch (Exception e) {
System.err.println("Exception in readTest: \n" + e);
}
}
public static void main(String[] args) {
EncryptedObjectStreamCipherX eos = new EncryptedObjectStreamCipherX();
eos.writeTest();
eos.readTest();
}
}
1 回答
这不需要
Cipher
流和SealedObject
,如果你考虑所有这些相对于Cipher
的实际执行顺序,你会发现它在写和读之间不对称 .丢失
SealedObject
,或丢失Cipher
流 .