AbstractCipherLayer.java
package org.jastacry.layer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.jastacry.JastacryException;
/**
* Abstract base class for encryption.
*
* <p>SPDX-License-Identifier: MIT
*
* @author Kai Kretschmann
*/
public abstract class AbstractCipherLayer extends AbstractBasicLayer
{
/**
* Block size.
*/
private static final int ONEBLOCKSIZE = 256;
/**
* How many bits in a byte.
*/
protected static final int BITSPERBYTE = 8;
/**
* PBEKeySpec.
*/
protected PBEKeySpec pbeKeySpec;
/**
* SecretKeyFactory.
*/
protected SecretKeyFactory keyFac;
/**
* SecretKey.
*/
protected SecretKey pbeKey;
/**
* SecretKeySpec.
*/
protected SecretKeySpec pbeSecretKeySpec;
/**
* ALG for the data.
*/
protected String strAlg;
/**
* Algorithm for the key.
*/
protected String strKeyAlg;
/**
* char array of password.
*/
protected char[] chPasswd;
/**
* Iterations count as defined by child class.
*/
protected int iterCount;
/**
* Key size as defined by child class.
*/
protected int currentKeysize;
/**
* IV length.
*/
protected int currentIvLen;
/**
* IV bytes.
*/
protected byte[] ivBytes;
/**
* Salt length.
*/
protected int currentSaltLen;
/**
* salt.
*/
protected byte[] salt;
/**
* Constructor of abstract class.
*
* @param cClass class name of calling class
* @param layerName name of real layer
*/
public AbstractCipherLayer(final Class<?> cClass, final String layerName)
{
super(cClass, layerName);
}
/**
* Abstract base method for getting algorithm name back.
*
* @return String
*/
protected abstract String getMyAlg();
/**
* Abstract base method for getting key algorithm name back.
*
* @return String
*/
protected abstract String getMyKeyAlg();
/**
* Abstract base method for getting salt len back.
*
* @return int length
*/
protected abstract int getMySaltLen();
/**
* Abstract base method for getting IV length back.
*
* @return int length
*/
protected abstract int getMyIvLen();
/**
* Abstract base method for getting a counter back.
*
* @return int
*/
protected abstract int getMyCount();
/**
* Abstract base method for getting key size back.
*
* @return int length
*/
protected abstract int getMyKeysize();
/**
* Generate random salt.
*/
protected final void getSalt()
{
salt = new byte[currentSaltLen];
new SecureRandom().nextBytes(salt);
}
/**
* store IV bytes.
*/
protected final void getIv()
{
ivBytes = new byte[currentIvLen];
}
/**
* Set base values via own getters, which are defined in child classes.
*/
protected final void init()
{
this.strAlg = getMyAlg();
this.strKeyAlg = getMyKeyAlg();
this.currentSaltLen = getMySaltLen();
this.currentIvLen = getMyIvLen();
this.iterCount = getMyCount();
this.currentKeysize = getMyKeysize();
}
/**
* Generate Keys from plain password.
*
* @throws JastacryException on error
*/
protected abstract void setupPbe() throws JastacryException;
/**
* Write IV data if any.
* @param outputStream stream to write to
* @param pbeCipher the cipher object
* @throws IOException in case of error
*/
private void writeIv(final OutputStream outputStream, final Cipher pbeCipher) throws IOException
{
if (0 == currentIvLen)
{
logger.trace("No IV to write");
}
else
{
ivBytes = pbeCipher.getIV();
if (null == ivBytes)
{
logger.error("IV empty");
}
else
{
outputStream.write(ivBytes, 0, currentIvLen);
} // if
} // if
}
/**
* Read IV data if any.
* @param inputStream stream to read from
* @throws IOException in case of error
*/
private void readIv(final InputStream inputStream) throws IOException
{
int iReadBytes;
if (0 == currentIvLen)
{
logger.trace("No IV to read");
}
else
{
ivBytes = new byte[currentIvLen];
iReadBytes = inputStream.read(ivBytes, 0, currentIvLen);
if (currentIvLen != iReadBytes)
{
logger.error("read {} bytes of IV, expecting {}.", iReadBytes, currentIvLen);
} // if
} // if
}
/**
* Write salt to stream.
* @param outputStream to write to
* @throws IOException in case of error
*/
private void writeSalt(final OutputStream outputStream) throws IOException
{
outputStream.write(salt, 0, currentSaltLen);
}
/**
* Read salt from stream.
* @param inputStream to read from
* @throws IOException in case of error
*/
private void readSalt(final InputStream inputStream) throws IOException
{
int iReadBytes;
salt = new byte[currentSaltLen];
iReadBytes = inputStream.read(salt, 0, currentSaltLen);
if (currentSaltLen != iReadBytes)
{
logger.error("read {} bytes of salt, expecting {}.", iReadBytes, currentSaltLen);
} // if
}
/**
* encode Stream function.
*
* @param inputStream incoming data
* @param outputStream outgoing data
* @throws JastacryException thrown on error
*/
@Override
public final void encStream(final InputStream inputStream, final OutputStream outputStream) throws JastacryException
{
Cipher pbeCipher;
try
{
getSalt();
setupPbe();
pbeCipher = Cipher.getInstance(strAlg);
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeSecretKeySpec);
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
final byte[] data = new byte[ONEBLOCKSIZE];
while ((nRead = inputStream.read(data, 0, data.length)) != -1)
{
buffer.write(data, 0, nRead);
}
buffer.flush();
final byte[] bInput = buffer.toByteArray();
// Encrypt the clear text
final byte[] ciphertext = pbeCipher.doFinal(bInput);
writeIv(outputStream, pbeCipher);
writeSalt(outputStream);
outputStream.write(ciphertext);
outputStream.close();
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
| JastacryException | BadPaddingException | IOException e)
{
logger.catching(e);
throw (JastacryException) new JastacryException("encStream failed").initCause(e);
}
}
/**
* decode Stream function.
*
* @param inputStream incoming data
* @param outputStream outgoing data
* @throws JastacryException thrown on error
*/
@Override
public final void decStream(final InputStream inputStream, final OutputStream outputStream) throws JastacryException
{
// Create PBE Cipher
Cipher pbeCipher;
try
{
readIv(inputStream);
readSalt(inputStream);
// call implementation of child class method.
setupPbe();
pbeCipher = Cipher.getInstance(strAlg);
if (0 == currentIvLen)
{
pbeCipher.init(Cipher.DECRYPT_MODE, pbeSecretKeySpec);
}
else
{
pbeCipher.init(Cipher.DECRYPT_MODE, pbeSecretKeySpec, new IvParameterSpec(ivBytes));
} // if
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead = 0;
final byte[] data = new byte[ONEBLOCKSIZE];
while ((nRead = inputStream.read(data, 0, data.length)) != -1)
{
buffer.write(data, 0, nRead);
}
buffer.flush();
final byte[] bInput = buffer.toByteArray();
// Encrypt the clear text
final byte[] ciphertext = pbeCipher.doFinal(bInput);
outputStream.write(ciphertext);
outputStream.close();
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
| BadPaddingException | InvalidAlgorithmParameterException | JastacryException | IOException e)
{
logger.catching(e);
throw (JastacryException) new JastacryException("decStream failed").initCause(e);
}
}
@Override
public final void encodeAndDecode(final InputStream inputStream, final OutputStream outputStream) throws JastacryException
{
throw new UnsupportedOperationException();
}
}