Search code examples
javaencryptionfile-io

java read encrypted Objects


I have an Class/Object Access which is Serializable.

public class Access implements Serializable {

    private static final long serialVersionUID = 1L;

    private URL website;
    private String username;
    private String password;

    // + some methods

    }

Now when writing them to a File I encrypt them using a Cipher. Looks like this:
WRITING:

ObservableList<Access> userData;
userData = FXCollections.observableArrayList();
...
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key128);
File file = new File("./resources/saves" + username);
file.createNewFile();
CipherOutputStream cipherOut = new CipherOutputStream(
        new BufferedOutputStream(new FileOutputStream(file, true)), cipher);
ObjectOutputStream out = new ObjectOutputStream(cipherOut);

userData.forEach((item) -> {
    try {
        out.writeObject(new SealedObject(item, cipher));
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
});
out.flush();
out.close();

READING:

ObservableList<Access> access = FXCollections.observableArrayList();

Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key128);

CipherInputStream cipherIn = new CipherInputStream(
        new BufferedInputStream(new FileInputStream("./resources/saves" + username)), cipher);
ObjectInputStream in = new ObjectInputStream(cipherIn);
SealedObject sealed;
while ((sealed = (SealedObject) in.readObject()) != null) {
    access.add((Access) sealed.getObject(cipher));
}

If I now Load the File it seems corrupted. I find it hard to find the mistake. I think the problem is in the load function. Am I missing something obvious here?

Error:

java.io.StreamCorruptedException: invalid stream header: 3D23898C

Thank you for your time & help!


Solution

  • Okay, The problem is the interaction between the cipher stream and the object stream.

    When you read the file, the ObjectInputStream asks to the underlying stream (the cipher stream) to read a very specific header. But the cipher stream has zero knowledge of that - he reads the normal amount of bytes he is supposed to, according his own protocol, in order to make sense of the encrypted data. The result is that the ObjectInputStream gets a truncated/altered header and can't process the stream correctly.

    Good news is, you don't actually need it ! The SealedObject will take care of encryption/decryption for you. Simply remove the cipher stream and it should work.

    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file, true));
    

    and to read:

        FileInputStream inputStream = new FileInputStream(fileName);
        ObjectInputStream ois = new ObjectInputStream(inputStream);
    

    Now, if you really want to use the cipher stream (and thus effectively encrypt/decrypt the data twice with the same key), you need first to make a "first pass" on the file to decrypt it, and then open an object stream on the new decrypted file.