java无法从密码输入流反序列化密封对象
为什么从CipherInputStream读取对象时会出现无效流头错误? (编辑:可能是由于重复使用了Cipher object?,下面包含的示例代码的新版本解决了此问题。)
我的程序正在尝试从ObjectInputStream读取数据,ObjectInputStream使用CipherInputStream作为源(源于文件),即:
文件->;解码流->;反序列化->;反对
运行时错误为:
java.io.StreamCorruptedException: invalid stream header: 73720019
下面列出的示例程序中的write方法使用普通FileOutputStream和作为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
其中“aced0005”对应于ObjectStream和版本(即头部):https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html
但是异常报告的“损坏”头对应于头之后的四个字节: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操作使用不同的密码对象: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 楼答案
这没有道理。你不需要^ {CD1> }流和 ^ {< CD2> },如果你考虑所有这些关于^ {CD1}}的实际执行顺序,你会发现它在写和读之间是不对称的。
丢失
SealedObject
,或丢失Cipher
流