All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schlichtherle.truezip.crypto.CipherOutputStream Maven / Gradle / Ivy

Go to download

The file system driver family for ZIP and related archive file types. Add the JAR artifact of this module to the run time class path to make its file system drivers available for service location in the client API modules.

There is a newer version: 7.7.10
Show newest version
/*
 * Copyright (C) 2005-2013 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package de.schlichtherle.truezip.crypto;

import de.schlichtherle.truezip.io.DecoratingOutputStream;
import edu.umd.cs.findbugs.annotations.CreatesObligation;
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import javax.annotation.WillCloseWhenClosed;
import javax.annotation.concurrent.NotThreadSafe;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.InvalidCipherTextException;

/**
 * Similar to {@code javax.crypto.CipherOutputStream} with some exceptions:
 * 
    *
  • This implementation is based on Bouncy Castle's lightweight crypto API * and uses a {@link BufferedBlockCipher} for ciphering. *
  • The {@link #cipher} used for encryption or decryption is accessible to * subclasses. *
  • The {@code flush()} method just flushes the underlying output stream * and has no effect on the cipher. *
  • A {@link #finish()} method has been added to allow finishing the output * (probably producing padding bytes) without closing the output. * This could be used in a subclass to produce a trailer with additional * information about the ciphered data (e.g. a MAC). *
* * @see CipherReadOnlyFile * @author Christian Schlichtherle */ @NotThreadSafe public class CipherOutputStream extends DecoratingOutputStream { /** The buffered block cipher used for preprocessing the output. */ protected @Nullable BufferedBlockCipher cipher; /** * The cipher output buffer used for preprocessing the output * to the decorated stream. * This buffer is autosized to the largest buffer written to this stream. */ private byte[] cipherOut = new byte[0]; /** * Creates a new cipher output stream. * Please note that unlike {@code javax.crypto.CipherOutputStream}, * the cipher does not need to be initialized before calling this * constructor. * However, the cipher must be initialized before anything is actually * written to this stream or before this stream is closed. * * @param out The output stream to write the encrypted or decrypted data to. * Maybe {@code null} for subsequent initialization by a sub-class. * @param cipher The cipher to use for encryption or decryption. * Maybe {@code null} for subsequent initialization by a sub-class. */ @CreatesObligation @edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION") public CipherOutputStream( final @CheckForNull @WillCloseWhenClosed OutputStream out, final @CheckForNull BufferedBlockCipher cipher) { super(out); this.cipher = cipher; } /** * Asserts that this cipher output stream is in open state, which requires * that {@link #cipher} is not {@code null}. * * @throws IOException If the preconditions do not hold. */ private void checkOpen() throws IOException { if (null == cipher) throw new IOException("cipher output stream is not in open state"); } /** * Ciphers and writes the given byte to the underlying output stream. * * @param b The byte to cipher and write. * @throws IOException If out or cipher aren't properly initialized, * the stream has been closed or an I/O error occured. */ @Override public void write(final int b) throws IOException { checkOpen(); int cipherLen = cipher.getUpdateOutputSize(1); byte[] cipherOut = this.cipherOut; if (cipherLen > cipherOut.length) this.cipherOut = cipherOut = new byte[cipherLen]; cipherLen = cipher.processByte((byte) b, cipherOut, 0); if (cipherLen > 0) delegate.write(cipherOut, 0, cipherLen); } /** * Ciphers and writes the contents of the given byte array to the * underlying output stream. * * @param buf The buffer holding the data to cipher and write. * @param off The start offset of the data in the buffer. * @param len The number of bytes to cipher and write. * @throws IOException If out or cipher aren't properly initialized, * the stream has been closed or an I/O error occured. */ @Override public void write(final byte[] buf, final int off, final int len) throws IOException { checkOpen(); int cipherLen = cipher.getUpdateOutputSize(len); byte[] cipherOut = this.cipherOut; if (cipherLen > cipherOut.length) this.cipherOut = cipherOut = new byte[cipherLen]; cipherLen = cipher.processBytes(buf, off, len, cipherOut, 0); delegate.write(cipherOut, 0, cipherLen); } /** * Finishes and voids this cipher output stream. * Calling this method causes all remaining buffered bytes to get written * and padding to get added if necessary. *

* Note that after a call to this method only {@link #close()} may get * called on this cipher output stream * The result of calling any other method (including this one) is undefined! * * @throws IOException If out or cipher aren't properly initialized, * the stream has been closed, an I/O error occured the cipher * text is invalid, i.e. required padding information is missing. */ @OverridingMethodsMustInvokeSuper protected void finish() throws IOException { checkOpen(); int cipherLen = cipher.getOutputSize(0); byte[] cipherOut = this.cipherOut; if (cipherLen > cipherOut.length) this.cipherOut = cipherOut = new byte[cipherLen]; try { cipherLen = cipher.doFinal(cipherOut, 0); } catch (InvalidCipherTextException ex) { throw new IOException(ex); } delegate.write(cipherOut, 0, cipherLen); } /** * Closes this output stream and releases any resources associated with it. * Upon the first call to this method, {@link #finish()} gets called and * {@link #cipher} gets set to {@code null} upon success. * Next, the {@link #delegate} gets unconditionally * {@linkplain #close() closed}. * * @throws IOException On any I/O failure. */ @Override public void close() throws IOException { if (null != cipher) { finish(); cipher = null; } delegate.close(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy