/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.pbe;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.io.CipherInputStream;
import org.bouncycastle.crypto.io.CipherOutputStream;
import org.bouncycastle.util.io.Streams;
import org.cryptacular.CryptoException;
/**
* Abstract base class for password-based encryption schemes based on salt data and iterated hashing as the basis of the
* key derivation function.
*
* <p>NOTE: Classes derived from this class are not thread safe. In particular, care should be take to prevent multiple
* threads from performing encryption and/or decryption concurrently.</p>
*
* @author Middleware Services
* @version $Revision: 2744 $
*/
public abstract class AbstractEncryptionScheme implements EncryptionScheme
{
/** Cipher used for encryption and decryption. */
private BufferedBlockCipher cipher;
/** Cipher initialization parameters. */
private CipherParameters parameters;
@Override
public byte[] encrypt(final byte[] plaintext)
{
cipher.init(true, parameters);
return process(plaintext);
}
@Override
public void encrypt(final InputStream in, final OutputStream out)
throws IOException
{
cipher.init(true, parameters);
Streams.pipeAll(in, new CipherOutputStream(out, cipher));
}
@Override
public byte[] decrypt(final byte[] ciphertext)
{
cipher.init(false, parameters);
return process(ciphertext);
}
@Override
public void decrypt(final InputStream in, final OutputStream out)
throws IOException
{
cipher.init(false, parameters);
Streams.pipeAll(new CipherInputStream(in, cipher), out);
}
/**
* Sets the block cipher used for encryption/decryption.
*
* @param bufferedBlockCipher Buffered block cipher.
*/
protected void setCipher(final BufferedBlockCipher bufferedBlockCipher)
{
if (bufferedBlockCipher == null) {
throw new IllegalArgumentException("Block cipher cannot be null");
}
this.cipher = bufferedBlockCipher;
}
/**
* Sets block cipher initialization parameters.
*
* @param parameters Cipher-specific init params.
*/
protected void setCipherParameters(final CipherParameters parameters)
{
if (parameters == null) {
throw new IllegalArgumentException("Cipher parameters cannot be null");
}
this.parameters = parameters;
}
/**
* Run the given data through the initialized underlying cipher and return the result.
*
* @param input Input data.
*
* @return Result of cipher acting on input.
*/
private byte[] process(final byte[] input)
{
final byte[] output = new byte[cipher.getOutputSize(input.length)];
int processed = cipher.processBytes(input, 0, input.length, output, 0);
try {
processed += cipher.doFinal(output, processed);
} catch (InvalidCipherTextException e) {
throw new CryptoException("Cipher error", e);
}
return Arrays.copyOfRange(output, 0, processed);
}
}