【发布时间】:2018-03-21 20:17:43
【问题描述】:
当我从 CipherInputStream 读取对象时,为什么会收到无效的流标头错误? (编辑:可能是由于重复使用 Cipher 对象?,下面包含的示例代码的新版本已经解决了这个问题。)
我的程序正在尝试从使用 CipherInputStream 作为源(来自文件)的 ObjectInputStream 中读取数据,即:
文件->解密流->反序列化->对象
运行时错误是:
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
其中“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 开始传送数据。
这是错误的输出:
跑: 读取对象(普通):测试 readTest 中的异常: java.io.StreamCorruptedException:无效的流标头:73720019 构建成功(总时间:0 秒)代码如下:
包加密对象流示例; 导入java.io.*; 导入java.security.*; 导入 javax.crypto.*; 导入静态 javax.crypto.Cipher.*; 导入 javax.crypto.spec.*; 公共类 EncryptedObjectStreamExample { 静态布尔密码=真; static final String fn = "C:/Temp/object."; 静态最终字节[] keyBytes = "MySecretPass1234".getBytes(); static final byte[] iv = "initialvector".getBytes(); 静态字符串 testObject = "测试"; 密码 c; 算法参数 ap; IvParameterSpec ivp; 键 k; 公共 EncryptedObjectStreamExample() { 尝试 { c = Cipher.getInstance("AES/CBC/PKCS5Padding"); ap = 算法参数.getInstance("AES"); ivp = new IvParameterSpec(iv); ap.init(ivp); k = new SecretKeySpec(keyBytes, "AES"); } 捕捉(异常前){ System.err.println("构造函数失败:\n" + ex); System.exit(1); } } 公共无效写测试(){ // 对象 -> 序列化 -> 密码流 -> 文件 尝试 { c.init(ENCRYPT_MODE, k, ap); 输出流 ostrp = new FileOutputStream(fn+"p"); OutputStream ostrx = new CipherOutputStream(new FileOutputStream(fn+"x"),c); 尝试(ObjectOutputStream oos = new ObjectOutputStream(ostrp)){ oos.writeObject(new SealedObject(testObject, c)); } 尝试 (ObjectOutputStream oos = new ObjectOutputStream(ostrx)) { oos.writeObject(new SealedObject(testObject, c)); } } 捕捉(异常 e){ System.err.println("writeTest 异常:\n" + e); } } 私人无效读取测试(){ // 文件 -> 解密流 -> 反序列化 -> 对象 尝试 { 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); 尝试 (ObjectInputStream ois = new ObjectInputStream(istrp)) { 字符串结果 = (String) (((SealedObject) ois.readObject()).getObject(c)); System.out.println("读取对象(普通):" + result); } 尝试 (ObjectInputStream ois = new ObjectInputStream(istrx)) { 字符串结果 = (String) (((SealedObject) ois.readObject()).getObject(c)); System.out.println("读取对象(加密):" + result); } } 捕捉(异常 e){ System.err.println("readTest 中的异常:\n" + e); } } 公共静态无效主要(字符串[]参数){ EncryptedObjectStreamExample eos = new EncryptedObjectStreamExample(); eos.writeTest(); eos.readTest(); } }编辑: 我重新调整了代码,以便对流和 Seal 操作使用不同的 Cipher 对象:x.sealdob 和 x.iostream
这个修改后的代码现在可以正常运行,两个文件都被写入和读取,没有错误:
这是新的(好的)输出:
跑: 读取对象(普通):测试 读取对象(加密):测试 构建成功(总时间:0 秒)这是更新后的代码(有效):
包加密对象流示例; 导入java.io.*; 导入java.security.*; 导入 java.security.spec.InvalidParameterSpecException; 导入 java.util.logging.Level; 导入 java.util.logging.Logger; 导入 javax.crypto.*; 导入静态 javax.crypto.Cipher.*; 导入 javax.crypto.spec.*; 公共类 EncryptedObjectStreamCipherX { 静态布尔密码=真; 静态最终字符串 FN = "C:/Temp/object."; 静态最终字节[] keyBytes = "MySecretPass1234".getBytes(); static final byte[] IV = "initialvector".getBytes(); 静态字符串 testObject = "测试"; 西弗X; 键 k; 私人类西弗{ 算法参数 ap; 密码iostream,sealedob; 静态最终字符串 ALG = "AES"; 西弗(){ 尝试 { iostream = Cipher.getInstance(ALG + "/CBC/PKCS5Padding"); sealob = Cipher.getInstance(ALG + "/CBC/PKCS5Padding"); ap = 算法参数.getInstance(ALG); ap.init(new IvParameterSpec(IV)); k = new SecretKeySpec(keyBytes, "AES"); } 捕捉(NoSuchPaddingException | InvalidParameterSpecException | NoSuchAlgorithmException ex){ Logger.getLogger(EncryptedObjectStreamCipherX.class.getName()).log(Level.SEVERE, null, ex); } } 无效加密模式(){ 尝试 { iostream.init(ENCRYPT_MODE, new SecretKeySpec(keyBytes, ALG), ap); sealob.init(ENCRYPT_MODE, new SecretKeySpec(keyBytes, ALG), ap); } 捕捉(InvalidKeyException | InvalidAlgorithmParameterException ex){ Logger.getLogger(EncryptedObjectStreamCipherX.class.getName()).log(Level.SEVERE, null, ex); } } 无效解密模式(){ 尝试 { iostream.init(DECRYPT_MODE, new SecretKeySpec(keyBytes, ALG), ap); sealob.init(DECRYPT_MODE, new SecretKeySpec(keyBytes, ALG), ap); } 捕捉(InvalidKeyException | InvalidAlgorithmParameterException ex){ Logger.getLogger(EncryptedObjectStreamCipherX.class.getName()).log(Level.SEVERE, null, ex); } } } 公共 EncryptedObjectStreamCipherX() { 尝试 { x = 新 Xipher(); } 捕捉(异常前){ System.err.println("构造函数失败:\n" + ex); System.exit(1); } } 公共无效写测试(){ // 对象 -> 序列化 -> 密码流 -> 文件 尝试 { x.encryptMode(); 输出流 ostrp = new FileOutputStream(FN + "p"); OutputStream ostrx = new CipherOutputStream(new FileOutputStream(FN + "x"), x.iostream); 尝试(ObjectOutputStream oos = new ObjectOutputStream(ostrp)){ oos.writeObject(new SealedObject(testObject, x.sealedob)); } 尝试 (ObjectOutputStream oos = new ObjectOutputStream(ostrx)) { oos.writeObject(new SealedObject(testObject, x.sealedob)); } } 捕捉(异常 e){ System.err.println("writeTest 异常:\n" + e); } } 私人无效读取测试(){ // 文件 -> 解密流 -> 反序列化 -> 对象 尝试 { x.decryptMode(); InputStream istrp = new FileInputStream("C:/Temp/object.p"); InputStream istrx = new CipherInputStream(new FileInputStream("C:/Temp/object.x"), x.iostream); 尝试 (ObjectInputStream ois = new ObjectInputStream(istrp)) { 字符串结果 = (String) (((SealedObject) ois.readObject()).getObject(x.sealedob)); System.out.println("读取对象(普通):" + result); } 尝试 (ObjectInputStream ois = new ObjectInputStream(istrx)) { 字符串结果 = (String) (((SealedObject) ois.readObject()).getObject(x.sealedob)); System.out.println("读取对象(加密):" + result); } } 捕捉(异常 e){ System.err.println("readTest 中的异常:\n" + e); } } 公共静态无效主要(字符串[]参数){ EncryptedObjectStreamCipherX eos = new EncryptedObjectStreamCipherX(); eos.writeTest(); eos.readTest(); } }【问题讨论】:
-
您是否尝试从加密文件中读取解密字节并将它们与您从未加密文件中读取的字节进行比较?比如长度?看起来你已经接近这样做了,但你只是在使用异常输出。
-
上面的代码写入然后读取2个文件,一个使用Cipher流,一个只是普通文件流,这正是为了说明问题,CipherStream版本读取失败。我想我现在很满意,这不是将流包装在流中或密封对象的问题,而是因为我使用相同的 Cipher 对象来初始化 Cipher Stream,就像我用来解封对象一样......不知何故它正在消耗标题 .. 可能是某种正在发生的事情?
标签: java objectinputstream objectoutputstream corrupt javax.crypto