无法从密码输入流反序列化密封对象

时间:2018-03-20 10:34:12

标签: java objectinputstream objectoutputstream corrupt javax.crypto

当我从CipherInputStream读取对象时,为什么会出现无效的流标头错误? (编辑:可能是由于重用了Cipher对象?,下面包含的新版本的示例代码已经解决了这个问题。)

我的程序正在尝试从使用CipherInputStream作为源(源自文件)的ObjectInputStream中读取,即:

档案 - > DeCipher Stream - > DeSerialize - >对象

运行时错误是:

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和Version(即标题):https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html

但是Exception报告的'corrupt'标题对应于标题后的四个字节: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 个答案:

答案 0 :(得分:0)

这没有意义。您不需要Cipher SealedObject,如果您考虑所有这些相对于{{1}的实际执行顺序你会发现它在写作和阅读之间并不对称。

丢失Cipher,或丢失SealedObject个流。