为什么当我从CipherInputStream读取对象时收到无效的流标头错误? (编辑:可能是由于重复使用密码对象?,下面包含的示例代码的新版本已解决了该问题。
我的程序正在尝试从使用CipherInputStream作为源(来自文件)的ObjectInputStream中读取,即:
文件 ->解密流 -> 反序列化 -> 对象
运行时错误为:
java.io.StreamCorruptedException: invalid stream header: 73720019
下面列出的示例程序中的 write 方法使用纯 FileOutputStream和CipherOutputStream 写入文件,因此我们有 2 个文件要检查。 read 方法同样尝试读取这两个文件。纯文件写入和读取没有问题,但是加密文件会引发异常。 如果您查看纯文件的前 8 个字节,您可以看到它们是:
0000000 254 355 005 s r 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 and Version(即标头):https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html
但是异常报告的"损坏"标头对应于标头后面的四个字节:73 72 00 19 !
在我看来,CipherInputStream 正在正确解密流,但消耗或跳过标头本身并从字节 5 开始交付数据。
这是错误的输出:
跑: 读取对象(普通):测试 读取测试中的异常: java.io.StreamCorruptedException: 无效的流标头: 73720019 生成成功(总时间:0 秒)
代码如下:
包加密对象流示例; 进口 java.io.*; 导入 java.security.*; 导入javax.crypto.*; 导入静态javax.crypto.Cipher.*; 导入 javax.crypto.spec.*; public class EncryptedObjectStreamExample { 静态布尔密码On = true; static final String fn = "C:/Temp/object."; static final byte[] keyBytes = "MySecretPass1234".getBytes(); 静态最终字节[] iv = "initialialvector".getBytes(); 静态字符串测试对象 = "测试"; 密码 c; 算法参数 ap; IvParameterSpec ivp; 键 k; public EncryptedObjectStreamExample() { 尝试 { 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:" + ex); 系统退出(1); } } public void writeTest() { 对象 -> 序列化 -> 密码流 -> 文件 尝试 { 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: " + e); } } private void readTest() { 文件 -> 解密流 -> 反序列化 -> 对象 尝试 { 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)) { 字符串结果 = (字符串) (((SealedObject) ois.readObject()).getObject(c)); System.out.println("Read Object (plain): " + result); } try (ObjectInputStream ois = new ObjectInputStream(istrx)) { 字符串结果 = (字符串) (((SealedObject) ois.readObject()).getObject(c)); System.out.println("Read Object (encrypt): " + result); } } catch (Exception e) { System.err.println("Exception in readTest: " + e); } } public static void main(String[] args) { EncryptedObjectStreamExample eos = new EncryptedObjectStreamExample(); eos.writeTest(); eos.readTest(); } }
编辑: 我重新调整了代码,以便为流和 Seal 操作使用不同的密码对象:x.sealdob 和 x.iostream
此修改后的代码现在可以正常运行,两个文件都已写入和读回,没有错误:
这是新的(良好)输出:
跑: 读取对象(普通):测试 读取对象(加密):测试 构建成功(总时间:0 秒)
以下是更新的代码(有效):
包加密对象流示例; 进口 java.io.*; 导入 java.security.*; import java.security.spec.InvalidParameterSpecException; 导入java.util.logging.Level; import java.util.logging.Logger; 导入javax.crypto.*; 导入静态javax.crypto.Cipher.*; 导入 javax.crypto.spec.*; public class EncryptedObjectStreamCipherX { 静态布尔密码On = true; 静态最终字符串 FN = "C:/Temp/object."; static final byte[] keyBytes = "MySecretPass1234".getBytes();static final byte[] IV = "initialialvector".getBytes(); 静态字符串测试对象 = "测试"; 西弗· 键 k; 私人课程 西弗 { 算法参数 ap; 密码 iostream, sealedob; 静态最终字符串 ALG = "AES"; Xipher() { 尝试 { 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 |无效参数规范异常 |NoSuchAlgorithmException ex) { Logger.getLogger(EncryptedObjectStreamCipherX.class.getName()).log(Level.SEVERE, null, ex); } } void encryptMode() { 尝试 { 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() { 尝试 { 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() { 尝试 { x = 新 Xipher(); } catch (Exception ex) { System.err.println("Failed Constructor:" + ex); 系统退出(1); } } public void writeTest() { 对象 -> 序列化 -> 密码流 -> 文件 尝试 { 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: " + e); } } private void readTest() { 文件 -> 解密流 -> 反序列化 -> 对象 尝试 { x.解密模式(); 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) (((SealedObject) ois.readObject()).getObject(x.sealedob)); System.out.println("Read Object (plain): " + result); } try (ObjectInputStream ois = new ObjectInputStream(istrx)) { 字符串结果 = (String) (((SealedObject) ois.readObject()).getObject(x.sealedob)); System.out.println("Read Object (encrypt): " + result); } } catch (Exception e) { System.err.println("Exception in readTest: " + e); } } public static void main(String[] args) { EncryptedObjectStreamCipherX eos = new EncryptedObjectStreamCipherX(); eos.writeTest(); eos.readTest(); } }
这没有意义。你不需要Cipher
流和SealedObject
,如果你考虑所有这些相对于Cipher
的实际执行顺序,你会发现它在写作和阅读之间是不对称的。
丢失SealedObject
或丢失Cipher
流。