Source:
public void testRecordAndRewindFirstPart(final SupportedCipherDetails cipherDetails) throws Exception { final SecretKey secretKey = generateSecretKey(cipherDetails); final EncryptionContext ctx = new EncryptionContext(secretKey, cipherDetails, true); final EncryptionState state = new EncryptionState(ctx); final byte[] iv = ctx.getCipher().getIV(); initializeEncryptionStateStreams(state); final EncryptionStateSnapshot snapshot = EncryptionStateRecorder.record(state, null); Assert.assertNotSame(snapshot.getCipher(), state.getEncryptionContext().getCipher()); final ByteArrayOutputStream originalOutput = new ByteArrayOutputStream(); final ByteArrayOutputStream snapshotOutput = new ByteArrayOutputStream(); final int inputSize = 11; final byte[] content = RND.nextBytes(inputSize); state.getMultipartStream().setNext(originalOutput); IOUtils.copy(new ByteArrayInputStream(content), state.getCipherStream()); state.getMultipartStream().flushBuffer(); originalOutput.write(ctx.getCipher().doFinal()); EncryptionStateRecorder.rewind(state, snapshot); state.getMultipartStream().setNext(snapshotOutput); IOUtils.copy(new ByteArrayInputStream(content), state.getCipherStream()); state.getMultipartStream().flushBuffer(); snapshotOutput.write(ctx.getCipher().doFinal()); assertByteArrayEquals(originalOutput.toByteArray(), snapshotOutput.toByteArray()); final Cipher decryptCipher = cipherDetails.getCipher(); decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, cipherDetails.getEncryptionParameterSpec(iv)); final byte[] originalDecrypted = decryptCipher.doFinal(originalOutput.toByteArray()); final byte[] snapshotDecrypted = decryptCipher.doFinal(snapshotOutput.toByteArray()); assertByteArrayEquals(originalDecrypted, content); assertByteArrayEquals(snapshotDecrypted, content); }

Target:
public void testRecordAndRewindFirstPart(final SupportedCipherDetails cipherDetails) throws Exception { final SecretKey secretKey = generateSecretKey(cipherDetails); final EncryptionContext ctx = new EncryptionContext(secretKey, cipherDetails, true); final EncryptionState state = new EncryptionState(ctx); final byte[] iv = ctx.getCipher().getIV(); initializeEncryptionStateStreams(state); final EncryptionStateSnapshot snapshot = EncryptionStateRecorder.record( state, null); final Cipher originalCipher = snapshot.getCipher(); final Cipher clonedCipher = state.getEncryptionContext().getCipher(); Assert.assertNotSame(originalCipher, clonedCipher); final ByteArrayOutputStream originalOutput = new ByteArrayOutputStream(); final ByteArrayOutputStream snapshotOutput = new ByteArrayOutputStream(); final int inputSize = RND_DATA.nextInt(300, 600); final byte[] content = new byte[inputSize]; RND.nextBytes(content); state.getMultipartStream().setNext(originalOutput); int originalByteCount = IOUtils.copy(new ByteArrayInputStream(content), state.getCipherStream()); originalOutput.write(state.getMultipartStream().getRemainder()); final byte[] finalOriginalBytes = ctx.getCipher().doFinal(); originalByteCount += finalOriginalBytes.length; originalOutput.write(finalOriginalBytes); EncryptionStateRecorder.rewind(state, snapshot); state.getMultipartStream().setNext(snapshotOutput); int snapshotByteCount = IOUtils.copy(new ByteArrayInputStream(content), state.getCipherStream()); snapshotOutput.write(state.getMultipartStream().getRemainder()); final byte[] finalSnapshotBytes = ctx.getCipher().doFinal(); snapshotByteCount += finalSnapshotBytes.length; snapshotOutput.write(finalSnapshotBytes); Assert.assertEquals(snapshotByteCount, originalByteCount, "The number of bytes recorded as encrypted differs"); assertByteArrayEquals(originalOutput.toByteArray(), snapshotOutput.toByteArray()); final Cipher decryptCipher = cipherDetails.getCipher(); decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, cipherDetails.getEncryptionParameterSpec(iv)); final byte[] originalDecrypted = decryptCipher.doFinal(originalOutput.toByteArray()); final byte[] snapshotDecrypted = decryptCipher.doFinal(snapshotOutput.toByteArray()); assertByteArrayEquals(originalDecrypted, content); assertByteArrayEquals(snapshotDecrypted, content); }