Data format:
* *+-----+----------+-------+------------+---------+ | Len | NonceLen | Nonce | KeyNameLen | KeyName | +-----+----------+-------+------------+---------+ ** *
Where fields are defined as follows:
* *The last two fields are optional and provide support for multiple keys at the encryption provider. A common case * for multiple keys is key rotation; by tagging encrypted data with a key name, an old key may be retrieved by name to * decrypt outstanding data which will be subsequently re-encrypted with a new key.
* * @author Middleware Services * * @deprecated Superseded by {@link CiphertextHeaderV2} */ @Deprecated public class CiphertextHeader { /** Maximum nonce length in bytes. */ protected static final int MAX_NONCE_LEN = 255; /** Maximum key name length in bytes. */ protected static final int MAX_KEYNAME_LEN = 500; /** Header nonce field value. */ protected final byte[] nonce; /** Header key name field value. */ protected String keyName; /** Header length in bytes. */ protected int length; /** * Creates a new instance with only a nonce. * * @param nonce Nonce bytes. */ public CiphertextHeader(final byte[] nonce) { this(nonce, null); } /** * Creates a new instance with a nonce and named key. * * @param nonce Nonce bytes. * @param keyName Key name. */ public CiphertextHeader(final byte[] nonce, final String keyName) { if (nonce.length > MAX_NONCE_LEN) { throw new IllegalArgumentException("Nonce exceeds size limit in bytes (" + MAX_NONCE_LEN + ")"); } if (keyName != null) { if (ByteUtil.toBytes(keyName).length > MAX_KEYNAME_LEN) { throw new IllegalArgumentException("Key name exceeds size limit in bytes (" + MAX_KEYNAME_LEN + ")"); } } this.nonce = nonce; this.keyName = keyName; length = computeLength(); } /** * Gets the header length in bytes. * * @return Header length in bytes. */ public int getLength() { return this.length; } /** * Gets the bytes of the nonce/IV. * * @return Nonce bytes. */ public byte[] getNonce() { return this.nonce; } /** * Gets the encryption key name stored in the header. * * @return Encryption key name. */ public String getKeyName() { return this.keyName; } /** * Encodes the header into bytes. * * @return Byte representation of header. */ public byte[] encode() { final ByteBuffer bb = ByteBuffer.allocate(length); bb.order(ByteOrder.BIG_ENDIAN); bb.putInt(length); bb.putInt(nonce.length); bb.put(nonce); if (keyName != null) { final byte[] b = keyName.getBytes(); bb.putInt(b.length); bb.put(b); } return bb.array(); } /** * @return Length of this header encoded as bytes. */ protected int computeLength() { int len = 8 + nonce.length; if (keyName != null) { len += 4 + keyName.getBytes().length; } return len; } /** * Creates a header from encrypted data containing a cleartext header prepended to the start. * * @param data Encrypted data with prepended header data. * * @return Decoded header. * * @throws EncodingException when ciphertext header cannot be decoded. */ public static CiphertextHeader decode(final byte[] data) throws EncodingException { final ByteBuffer bb = ByteBuffer.wrap(data); bb.order(ByteOrder.BIG_ENDIAN); final int length = bb.getInt(); if (length < 0) { throw new EncodingException("Bad ciphertext header"); } final byte[] nonce; int nonceLen = 0; try { nonceLen = bb.getInt(); if (nonceLen > MAX_NONCE_LEN) { throw new EncodingException("Bad ciphertext header: maximum nonce length exceeded"); } nonce = new byte[nonceLen]; bb.get(nonce); } catch (IndexOutOfBoundsException | BufferUnderflowException e) { throw new EncodingException("Bad ciphertext header"); } String keyName = null; if (length > nonce.length + 8) { final byte[] b; int keyLen = 0; try { keyLen = bb.getInt(); if (keyLen > MAX_KEYNAME_LEN) { throw new EncodingException("Bad ciphertext header: maximum key length exceeded"); } b = new byte[keyLen]; bb.get(b); keyName = new String(b); } catch (IndexOutOfBoundsException | BufferUnderflowException e) { throw new EncodingException("Bad ciphertext header"); } } return new CiphertextHeader(nonce, keyName); } /** * Creates a header from encrypted data containing a cleartext header prepended to the start. * * @param input Input stream that is positioned at the start of ciphertext header data. * * @return Decoded header. * * @throws EncodingException when ciphertext header cannot be decoded. * @throws StreamException on stream IO errors. */ public static CiphertextHeader decode(final InputStream input) throws EncodingException, StreamException { final int length = ByteUtil.readInt(input); if (length < 0) { throw new EncodingException("Bad ciphertext header"); } final byte[] nonce; int nonceLen = 0; try { nonceLen = ByteUtil.readInt(input); if (nonceLen > MAX_NONCE_LEN) { throw new EncodingException("Bad ciphertext header: maximum nonce size exceeded"); } nonce = new byte[nonceLen]; input.read(nonce); } catch (ArrayIndexOutOfBoundsException e) { throw new EncodingException("Bad ciphertext header"); } catch (IOException e) { throw new StreamException(e); } String keyName = null; if (length > nonce.length + 8) { final byte[] b; int keyLen = 0; try { keyLen = ByteUtil.readInt(input); if (keyLen > MAX_KEYNAME_LEN) { throw new EncodingException("Bad ciphertext header: maximum key length exceeded"); } b = new byte[keyLen]; input.read(b); } catch (ArrayIndexOutOfBoundsException e) { throw new EncodingException("Bad ciphertext header"); } catch (IOException e) { throw new StreamException(e); } keyName = new String(b); } return new CiphertextHeader(nonce, keyName); } } cryptacular-1.2.5/src/main/java/org/cryptacular/CiphertextHeaderV2.java 0000664 0000000 0000000 00000023261 14226043123 0026130 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.function.BiConsumer; import java.util.function.Function; import javax.crypto.SecretKey; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.macs.HMac; import org.cryptacular.util.ByteUtil; /** * Cleartext header prepended to ciphertext providing data required for decryption. * *Data format:
* *+---------+---------+---+----------+-------+------+ | Version | KeyName | 0 | NonceLen | Nonce | HMAC | +---------+---------+---+----------+-------+------+ | | +--- 4 ---+--- x ---+ 1 +--- 1 ----+-- y --+- 32 -+ ** *
Where fields are defined as follows:
* *The last two fields provide support for multiple keys at the encryption provider. A common case for multiple * keys is key rotation; by tagging encrypted data with a key name, an old key may be retrieved by name to decrypt * outstanding data which will be subsequently re-encrypted with a new key.
* * @author Middleware Services */ public class CiphertextHeaderV2 extends CiphertextHeader { /** Header version format. */ private static final int VERSION = -2; /** Size of HMAC algorithm output in bytes. */ private static final int HMAC_SIZE = 32; /** Function to resolve a key from a symbolic key name. */ private Functionoutput.length
bytes from the input stream into the output buffer.
*
* @param input Input stream.
* @param output Output buffer.
*
* @throws StreamException on stream IO errors.
*/
private static void readInto(final InputStream input, final byte[] output)
{
try {
input.read(output);
} catch (IOException e) {
throw new StreamException(e);
}
}
/**
* Read a single byte from the input stream.
*
* @param input Input stream.
*
* @return Byte read from input stream.
*/
private static byte readByte(final InputStream input)
{
try {
return (byte) input.read();
} catch (IOException e) {
throw new StreamException(e);
}
}
/**
* Determines if two byte array ranges are equal bytewise.
*
* @param a First array to compare.
* @param aOff Offset into first array.
* @param b Second array to compare.
* @param bOff Offset into second array.
* @param length Number of bytes to compare.
*
* @return True if every byte in the given range is equal, false otherwise.
*/
private static boolean arraysEqual(final byte[] a, final int aOff, final byte[] b, final int bOff, final int length)
{
if (length + aOff > a.length || length + bOff > b.length) {
return false;
}
for (int i = 0; i < length; i++) {
if (a[i + aOff] != b[i + bOff]) {
return false;
}
}
return true;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/CryptoException.java 0000664 0000000 0000000 00000001336 14226043123 0025626 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular;
/**
* Runtime error describing a generic cryptographic problem (e.g. bad padding, unsupported cipher).
*
* @author Middleware Services
*/
public class CryptoException extends RuntimeException
{
/**
* Creates a new instance with the given error message.
*
* @param message Error message.
*/
public CryptoException(final String message)
{
super(message);
}
/**
* Creates a new instance with the given error message and cause.
*
* @param message Error message.
* @param cause Error cause.
*/
public CryptoException(final String message, final Throwable cause)
{
super(message, cause);
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/EncodingException.java 0000664 0000000 0000000 00000001364 14226043123 0026075 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular;
/**
* Runtime error describing an encoding problem of a cryptographic primitive (e.g. private key, X.509 certificate).
*
* @author Middleware Services
*/
public class EncodingException extends RuntimeException
{
/**
* Creates a new instance with the given error message.
*
* @param message Error message.
*/
public EncodingException(final String message)
{
super(message);
}
/**
* Creates a new instance with the given error message and cause.
*
* @param message Error message.
* @param cause Error cause.
*/
public EncodingException(final String message, final Throwable cause)
{
super(message, cause);
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/SaltedHash.java 0000664 0000000 0000000 00000006340 14226043123 0024507 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular;
import org.cryptacular.codec.Encoder;
import org.cryptacular.util.CodecUtil;
/**
* Container for the output of a salted hash operation that includes both the digest output and salt value.
*
* @author Middleware Services
*/
public class SaltedHash
{
/** Digest output. */
private final byte[] hash;
/** Salt value. */
private final byte[] salt;
/**
* Creates a new instance with digest and salt data.
*
* @param hash Digest output.
* @param salt Salt value used to compute salt.
*/
public SaltedHash(final byte[] hash, final byte[] salt)
{
this.hash = hash;
this.salt = salt;
}
/**
* Creates a new instance from byte input that contains the concatenation of digest output and salt.
*
* @param hashWithSalt Concatenation of hash and salt.
* @param digestLength Number of bytes in digest output.
* @param toEnd True if salt is appended to end of hash, false if salt is prepended to hash.
*/
public SaltedHash(final byte[] hashWithSalt, final int digestLength, final boolean toEnd)
{
this.hash = new byte[digestLength];
this.salt = new byte[hashWithSalt.length - digestLength];
if (toEnd) {
System.arraycopy(hashWithSalt, 0, hash, 0, hash.length);
System.arraycopy(hashWithSalt, hash.length, salt, 0, salt.length);
} else {
System.arraycopy(hashWithSalt, 0, salt, 0, salt.length);
System.arraycopy(hashWithSalt, salt.length, hash, 0, hash.length);
}
}
/** @return Digest output. */
public byte[] getHash()
{
return hash;
}
/** @return Salt value. */
public byte[] getSalt()
{
return salt;
}
/**
* Gets N bytes of salt.
*
* @param n Number of bytes of salt; must be less than or equal to salt size.
*
* @return First N bytes of salt.
*/
public byte[] getSalt(final int n)
{
if (n > salt.length) {
throw new IllegalArgumentException("Requested size exceeded length: " + n + ">" + salt.length);
}
final byte[] bytes = new byte[n];
System.arraycopy(salt, 0, bytes, 0, n);
return bytes;
}
/**
* Gets an encoded string of the concatenation of digest output and salt.
*
* @param toEnd True to append salt to end of hash, false to prefix hash with salt.
* @param encoder Encodes concatenated bytes to a string.
*
* @return Salt concatenated to hash encoded as a string.
*/
public String concatenateSalt(final boolean toEnd, final Encoder encoder)
{
return CodecUtil.encode(encoder, concatenateSalt(toEnd));
}
/**
* Gets a byte array containing the concatenation of digest output and salt.
*
* @param toEnd True to append salt to end of hash, false to prefix hash with salt.
*
* @return Salt concatenated to hash.
*/
public byte[] concatenateSalt(final boolean toEnd)
{
final byte[] output = new byte[hash.length + salt.length];
if (toEnd) {
System.arraycopy(hash, 0, output, 0, hash.length);
System.arraycopy(salt, 0, output, hash.length, salt.length);
} else {
System.arraycopy(salt, 0, output, 0, salt.length);
System.arraycopy(hash, 0, output, salt.length, hash.length);
}
return output;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/StreamException.java 0000664 0000000 0000000 00000001317 14226043123 0025600 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular;
import java.io.IOException;
/**
* Runtime exception thrown on stream IO errors. Effectively a runtime equivalent of {@link java.io.IOException}.
*
* @author Middleware Services
*/
public class StreamException extends RuntimeException
{
/**
* Creates a new instance with the given error message.
*
* @param message Error message.
*/
public StreamException(final String message)
{
super(message);
}
/**
* Creates a new instance with causing IO exception.
*
* @param cause IO exception to wrap.
*/
public StreamException(final IOException cause)
{
super("IO error", cause);
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/adapter/ 0000775 0000000 0000000 00000000000 14226043123 0023241 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/main/java/org/cryptacular/adapter/AEADBlockCipherAdapter.java 0000664 0000000 0000000 00000003517 14226043123 0030213 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.adapter;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.cryptacular.CryptoException;
/**
* Adapts a {@link AEADBlockCipherAdapter}.
*
* @author Middleware Services
*/
public class AEADBlockCipherAdapter implements BlockCipherAdapter
{
/** All methods delegate to this instance. */
private final AEADBlockCipher cipherDelegate;
/**
* Creates a new instance that delegates to the given cipher.
*
* @param delegate Adapted cipher.
*/
public AEADBlockCipherAdapter(final AEADBlockCipher delegate)
{
cipherDelegate = delegate;
}
@Override
public int getOutputSize(final int len)
{
return cipherDelegate.getOutputSize(len);
}
@Override
public void init(final boolean forEncryption, final CipherParameters params) throws CryptoException
{
try {
cipherDelegate.init(forEncryption, params);
} catch (RuntimeException e) {
throw new CryptoException("Cipher initialization error", e);
}
}
@Override
public int processBytes(final byte[] in, final int inOff, final int len, final byte[] out, final int outOff)
throws CryptoException
{
try {
return cipherDelegate.processBytes(in, inOff, len, out, outOff);
} catch (RuntimeException e) {
throw new CryptoException("Cipher processing error", e);
}
}
@Override
public int doFinal(final byte[] out, final int outOff) throws CryptoException
{
try {
return cipherDelegate.doFinal(out, outOff);
} catch (InvalidCipherTextException e) {
throw new CryptoException("Error finalizing cipher", e);
}
}
@Override
public void reset()
{
cipherDelegate.reset();
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/adapter/AbstractWrappedDSAKey.java 0000664 0000000 0000000 00000002337 14226043123 0030200 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.adapter;
import java.math.BigInteger;
import java.security.interfaces.DSAParams;
import org.bouncycastle.crypto.params.DSAKeyParameters;
/**
* Base class for DSA wrapped keys.
*
* @param * ECPrivateKey ::= SEQUENCE { * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), * privateKey OCTET STRING, * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, * publicKey [1] BIT STRING OPTIONAL * } ** * @param seq ASN1 sequence to parse * * @return EC private key */ private ECPrivateKeyParameters parseECPrivateKey(final ASN1Sequence seq) { final ASN1TaggedObject asn1Params = ASN1TaggedObject.getInstance(seq.getObjectAt(2)); final X9ECParameters params; if (asn1Params.getObject() instanceof ASN1ObjectIdentifier) { params = ECUtil.getNamedCurveByOid(ASN1ObjectIdentifier.getInstance(asn1Params.getObject())); } else { params = X9ECParameters.getInstance(asn1Params.getObject()); } return new ECPrivateKeyParameters( new BigInteger(1, ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets()), new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH(), params.getSeed())); } } cryptacular-1.2.5/src/main/java/org/cryptacular/asn/PKCS8PrivateKeyDecoder.java 0000664 0000000 0000000 00000003700 14226043123 0027367 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.asn; import java.io.IOException; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo; import org.bouncycastle.asn1.pkcs.PBEParameter; import org.bouncycastle.asn1.pkcs.PBES2Parameters; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.util.PrivateKeyFactory; import org.cryptacular.EncodingException; import org.cryptacular.pbe.EncryptionScheme; import org.cryptacular.pbe.PBES1Algorithm; import org.cryptacular.pbe.PBES1EncryptionScheme; import org.cryptacular.pbe.PBES2EncryptionScheme; /** * Decodes PEM or DER-encoded PKCS#8 private keys. * * @author Middleware Services */ public class PKCS8PrivateKeyDecoder extends AbstractPrivateKeyDecoder
* $2n$cost$xxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
*
* where:
* n is an optional bcrypt algorithm version (typically "a" or "b")
* 4 ≤ cost ≤ 31
* x is 22 characters of encoded salt
* y is 31 characters of encoded hash bytes
*
* * The encoding for salt and hash bytes is a variant of base-64 encoding without padding in the following alphabet: *
*./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
*
* @author Middleware Services
*/
public class BCryptHashBean implements HashBean$2n$cost$xxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
* given a salt and a password.
* @param data A 2-element array containing salt and password. The salt may be encoded per the bcrypt standard
* or raw bytes.
*
* @return An encoded bcrypt hash, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
in the specification above.
*
* @throws CryptoException on bcrypt algorithm errors.
*/
@Override
public String hash(final Object... data) throws CryptoException
{
if (data.length != 2) {
throw new IllegalArgumentException("Expected exactly two elements in data array but got " + data.length);
}
return encode(BCrypt.generate(password(version, data[1]), salt(data[0]), cost), 23);
}
/**
* Compares a bcrypt hash of the form $2n$cost$xxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
* with the computed hash from the given password. The bcrypt algorithm parameters are derived from the reference
* bcrypt hash string.
*
* @param data A 1-element array containing password.
*
* @return True if the computed hash is exactly equal to the reference hash, false otherwise.
*
* @throws CryptoException on bcrypt algorithm errors.
*/
@Override
public boolean compare(final String hash, final Object... data) throws CryptoException, StreamException
{
if (data.length != 1) {
throw new IllegalArgumentException("Expected exactly one element in data array but got " + data.length);
}
final BCryptParameters params = new BCryptParameters(hash);
final byte[] computed = BCrypt.generate(password(params.getVersion(), data[0]), params.getSalt(), params.getCost());
for (int i = 0; i < 23; i++) {
if (params.getHash()[i] != computed[i]) {
return false;
}
}
return true;
}
/**
* Encodes an input byte array into a string using the configured encoder.
*
* @param bytes Input bytes to encode.
* @param length Number of bytes of input to encode.
*
* @return Input encoded as a string.
*/
private static String encode(final byte[] bytes, final int length)
{
final Encoder encoder = new Base64Encoder.Builder().setAlphabet(ALPHABET).setPadding(false).build();
// Only want 184 bits (23 bytes) of the output
final ByteBuffer input = ByteBuffer.wrap(bytes, 0, length);
final CharBuffer output = CharBuffer.allocate(encoder.outputSize(length));
encoder.encode(input, output);
encoder.finalize(output);
return output.flip().toString();
}
/**
* Decodes an input string into a byte array using the configured decoder.
*
* @param input Input string to decode.
* @param length Desired output size in bytes.
*
* @return Input decoded as a byte array.
*/
private static byte[] decode(final String input, final int length)
{
final Decoder decoder = new Base64Decoder.Builder().setAlphabet(ALPHABET).setPadding(false).build();
final ByteBuffer output = ByteBuffer.allocate(decoder.outputSize(input.length()));
decoder.decode(CharBuffer.wrap(input), output);
decoder.finalize(output);
output.flip();
if (output.limit() != length) {
throw new IllegalArgumentException("Input is not of the expected size: " + output.limit() + "!=" + length);
}
return ByteUtil.toArray(output);
}
/**
* Converts an input object into a salt as an array of bytes.
*
* @param data Input salt as a byte array or encoded string.
*
* @return Salt as byte array.
*/
private static byte[] salt(final Object data)
{
if (data instanceof byte[]) {
return (byte[]) data;
} else if (data instanceof String) {
return decode((String) data, 16);
}
throw new IllegalArgumentException("Expected byte array or base-64 string.");
}
/**
* Converts an input object into a password as an array of UTF-8 bytes. A null terminator is added if the supplied
* data does not end with one.
*
* @param version Bcrypt version, e.g. "2a".
* @param data Input password.
*
* @return Null terminated password as UTF-8 byte array.
*/
private static byte[] password(final String version, final Object data)
{
if (data instanceof byte[]) {
final byte[] origData = (byte[]) data;
final byte[] newData;
if (origData[origData.length - 1] != 0x00) {
newData = new byte[origData.length + 1];
System.arraycopy(origData, 0, newData, 0, origData.length);
newData[newData.length - 1] = 0x00;
} else {
newData = origData;
}
return newData;
}
final StringBuilder sb = new StringBuilder();
if (data instanceof char[]) {
sb.append((char[]) data);
} else if (data instanceof String) {
sb.append((String) data);
} else {
throw new IllegalArgumentException("Expected byte array or string.");
}
if (sb.charAt(sb.length() - 1) != '\0') {
// Version 2a and later requires null terminator on password
sb.append('\0');
}
return sb.toString().getBytes(StandardCharsets.UTF_8);
}
/**
* Handles encoding and decoding a bcrypt hash of the form
* $2n$cost$xxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
.
*/
public static class BCryptParameters
{
/** bcrypt version. */
private final String version;
/** bcrypt cost. */
private final int cost;
/** bcrypt salt. */
private final byte[] salt;
/** bcrypt hash. */
private final byte[] hash;
/**
* Decodes bcrypt parameters from a string.
*
* @param bCryptString bcrypt hash of the form
* $2n$cost$xxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
*/
protected BCryptParameters(final String bCryptString)
{
if (!bCryptString.startsWith("$2")) {
throw new IllegalArgumentException("Expected bcrypt hash of the form $2n$cost$salthash");
}
final String[] parts = bCryptString.split("\\$");
if (parts.length != 4) {
throw new IllegalArgumentException("Invalid bcrypt hash");
}
version = parts[1];
cost = Integer.parseInt(parts[2]);
salt = decode(parts[3].substring(0, 22), 16);
hash = decode(parts[3].substring(22), 23);
}
/** @return bcrypt version. */
public String getVersion()
{
return version;
}
/** @return bcrypt cost in the range [4, 31]. */
public int getCost()
{
return cost;
}
/** @return bcrypt salt. */
public byte[] getSalt()
{
return salt;
}
/** @return bcrypt hash. */
public byte[] getHash()
{
return hash;
}
/**
* Produces an encoded bcrypt hash string from bcrypt parameter data.
*
* @return Bcrypt hash of the form $2n$cost$xxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
.
*/
public String encode()
{
return new StringBuilder(60)
.append('$')
.append(version)
.append('$')
.append(cost)
.append('$')
.append(BCryptHashBean.encode(salt, 16))
.append(BCryptHashBean.encode(hash, 23))
.toString();
}
/**
* Produces an encoded bcrypt hash string from bcrypt parameters and a provided hash string.
*
* @param hash Encoded bcrypt hash bytes; e.g. the value produced from {@link #hash(Object...)}.
*
* @return Bcrypt hash of the form $2n$cost$xxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
.
*/
public String encode(final String hash)
{
return new StringBuilder(60)
.append('$')
.append(version)
.append('$')
.append(cost)
.append('$')
.append(BCryptHashBean.encode(salt, 16))
.append(hash)
.toString();
}
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/bean/BufferedBlockCipherBean.java 0000664 0000000 0000000 00000005224 14226043123 0030012 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.security.KeyStore;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.cryptacular.CiphertextHeader;
import org.cryptacular.adapter.BufferedBlockCipherAdapter;
import org.cryptacular.generator.Nonce;
import org.cryptacular.spec.Spec;
/**
* Cipher bean that performs symmetric encryption/decryption using a standard block cipher in a standard mode (e.g. CBC,
* OFB) with padding to support processing inputs of arbitrary length.
*
* @author Middleware Services
*/
public class BufferedBlockCipherBean extends AbstractBlockCipherBean
{
/** Block cipher specification (algorithm, mode, padding). */
private SpecThe caller is responsible for providing and managing the streams (e.g. closing them when finished).
* * @param input Input stream containing plaintext data to encrypt. * @param output Output stream containing ciphertext produced by cipher in encryption mode. * * @throws CryptoException on underlying cipher data handling errors. * @throws StreamException on stream IO errors. */ void encrypt(InputStream input, OutputStream output) throws CryptoException, StreamException; /** * Decrypts the input data using a block cipher. * * @param input Ciphertext data to encrypt. * * @return Plaintext output. * * @throws CryptoException on underlying cipher data handling errors. */ byte[] decrypt(byte[] input) throws CryptoException; /** * Decrypts the data from the input stream onto the output stream using a symmetric cipher. * *The caller is responsible for providing and managing the streams (e.g. closing them when finished).
* * @param input Input stream containing ciphertext data to decrypt. * @param output Output stream containing plaintext produced by cipher in decryption mode. * * @throws CryptoException on underlying cipher data handling errors. * @throws StreamException on stream IO errors. */ void decrypt(InputStream input, OutputStream output) throws CryptoException, StreamException; } cryptacular-1.2.5/src/main/java/org/cryptacular/bean/EncodingHashBean.java 0000664 0000000 0000000 00000012021 14226043123 0026505 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.bean; import org.bouncycastle.crypto.Digest; import org.bouncycastle.util.Arrays; import org.cryptacular.CryptoException; import org.cryptacular.EncodingException; import org.cryptacular.StreamException; import org.cryptacular.codec.Codec; import org.cryptacular.spec.Spec; import org.cryptacular.util.CodecUtil; /** * Computes a hash in an encoded format, e.g. hex, base64. * * @author Middleware Services */ public class EncodingHashBean extends AbstractHashBean implements HashBeanbyte[]
,
* {@link CharSequence}, {@link java.io.InputStream}, and {@link org.cryptacular.io.Resource}. Unless
* otherwise noted, character data is processed in the UTF-8
character set; if another
* character set is desired, the caller should convert to byte[]
and provide the resulting
* bytes.
*
* @return Digest output.
*
* @throws CryptoException on hash computation errors.
* @throws StreamException on stream IO errors.
*/
T hash(Object... data) throws CryptoException, StreamException;
/**
* Compares a known hash value with the hash of the given data.
*
* @param hash Known hash value.
* @param data Data to hash.
*
* @return True if the hashed data matches the given hash, false otherwise.
*
* @throws CryptoException on hash computation errors.
* @throws StreamException on stream IO errors.
*/
boolean compare(T hash, Object... data) throws CryptoException, StreamException;
}
cryptacular-1.2.5/src/main/java/org/cryptacular/bean/KeyStoreBasedKeyFactoryBean.java 0000664 0000000 0000000 00000005234 14226043123 0030670 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import org.cryptacular.CryptoException;
/**
* Factory that produces either a {@link javax.crypto.SecretKey} or {@link java.security.PrivateKey}.
*
* from a {@link KeyStore}.
* * @param-----BEGIN PUBLIC KEY----- MIIBtzCCASsGByqGSM44BAEwggEeAoGBAOulifG+AGGBVGWEjunG4661rydB7eFy RfHzbwVAVaPU0H3zFcOY35z1l6Pk4ZANVHq7hCbViJBR7XyrkYKaUcaB0nSPLgg3 vWWOmvGqhuR6tWRGbz4fyHl1urCRk9mrJum4mAJd3OlLugCyuIqozsYUtvJ5mlGe vir1zmxinKd7AhUA7fBEySYP53g7FLOlcEyuhIjvQAECgYBJ9baoGzn0zKpeteC4 jfbGVuKrFksr2eeY0AFJOeTtyFkCnVqrNnF674eN1RAOwA2tzzhWZ96G0AGux8ah mGsNRbj/qaUTDNRWr7BPBIvDd+8LpMin4Cb5j4c/A7uOY+5WxhUm3TNifueBRohw h1NnexYQqpclcuTRA/ougLX48gOBhQACgYEA6Tw2khtb1g0vcHu6JRgggWPZVTuj /HOH3FyjufsfHogWKrlKebZ6hnQ73qAcEgLLYKctPdCX6wnpXN+BsQGYdTkc0FsU NZD4VW5L5kaWRiLVfE8x55wXdMZtXKWqg1vL6aXYZw7RFe9U9Ck+/AG90knThDC+ xrX2FTDm6uC25rk= -----END PUBLIC KEY-----* * @author Middleware Services * @see KeyPairUtil#decodePublicKey(byte[]) */ public class PemBasedPublicKeyFactoryBean implements FactoryBean
ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
.
*/
public Base32Codec()
{
encoder = new Base32Encoder();
decoder = new Base32Decoder();
customAlphabet = null;
padding = true;
}
/**
* Creates a new instance using the given 32-character alphabet.
*
* @param alphabet 32-character alphabet to use.
*/
public Base32Codec(final String alphabet)
{
this(alphabet, true);
}
/**
* Creates a new instance using the given 32-character alphabet with option to enable/disable padding.
*
* @param alphabet 32-character alphabet to use.
* @param inputOutputPadding True to enable support for padding, false otherwise.
*/
public Base32Codec(final String alphabet, final boolean inputOutputPadding)
{
customAlphabet = alphabet;
padding = inputOutputPadding;
encoder = newEncoder();
decoder = newDecoder();
}
@Override
public Encoder getEncoder()
{
return encoder;
}
@Override
public Decoder getDecoder()
{
return decoder;
}
@Override
public Encoder newEncoder()
{
final Base32Encoder encoder;
if (customAlphabet != null) {
encoder = new Base32Encoder(customAlphabet);
} else {
encoder = new Base32Encoder();
}
encoder.setPaddedOutput(padding);
return encoder;
}
@Override
public Decoder newDecoder()
{
final Base32Decoder decoder;
if (customAlphabet != null) {
decoder = new Base32Decoder(customAlphabet);
} else {
decoder = new Base32Decoder();
}
decoder.setPaddedInput(padding);
return decoder;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/Base32Decoder.java 0000664 0000000 0000000 00000002046 14226043123 0026050 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
/**
* Stateful base 32 decoder with support for line breaks.
*
* @author Middleware Services
*/
public class Base32Decoder extends AbstractBaseNDecoder
{
/** Base-32 character decoding table. */
private static final byte[] DECODING_TABLE;
/* Initializes the character decoding table. */
static
{
DECODING_TABLE = decodingTable("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", 32);
}
/**
* Creates a new instance using the RFC 4648 alphabet, ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
, for decoding.
*/
public Base32Decoder()
{
super(DECODING_TABLE);
}
/**
* Creates a new instance using the given 32-character alphabet for decoding.
*
* @param alphabet 32-character alphabet to use.
*/
public Base32Decoder(final String alphabet)
{
super(decodingTable(alphabet, 32));
}
@Override
protected int getBlockLength()
{
return 40;
}
@Override
protected int getBitsPerChar()
{
return 5;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/Base32Encoder.java 0000664 0000000 0000000 00000004130 14226043123 0026056 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
/**
* Stateful base 32 encoder with support for configurable line breaks.
*
* @author Middleware Services
*/
public class Base32Encoder extends AbstractBaseNEncoder
{
/** Base 32 character encoding table. */
private static final char[] ENCODING_TABLE;
/* Initializes the default character encoding table. */
static
{
ENCODING_TABLE = encodingTable("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", 32);
}
/**
* Creates a new instance that produces base 32-encoded output in the RFC 4648 alphabet,
* ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
, with no line breaks in the output.
*/
public Base32Encoder()
{
// Default to no line breaks.
this(-1);
}
/**
* Creates a new instance that produces base 32-encoded output in the RFC 4648 alphabet,
* ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
, with the given number of characters per line in the output.
*
* @param charactersPerLine Number of characters per line. A zero or negative value disables line breaks.
*/
public Base32Encoder(final int charactersPerLine)
{
super(ENCODING_TABLE, charactersPerLine);
}
/**
* Creates a new instance that produces base 32-encoded output in the given 32-character alphabet with no line
* breaks in the output.
*
* @param alphabet 32-character alphabet to use.
*/
public Base32Encoder(final String alphabet)
{
this(alphabet, -1);
}
/**
* Creates a new instance that produces base 32-encoded output in the given 32-character alphabet
* with the given number of characters per line in the output.
*
* @param alphabet 32-character alphabet to use.
* @param charactersPerLine Number of characters per line. A zero or negative value disables line breaks.
*/
public Base32Encoder(final String alphabet, final int charactersPerLine)
{
super(encodingTable(alphabet, 32), charactersPerLine);
}
@Override
protected int getBlockLength()
{
return 40;
}
@Override
protected int getBitsPerChar()
{
return 5;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/Base64Codec.java 0000664 0000000 0000000 00000004077 14226043123 0025533 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
/**
* Base 64 encoder/decoder pair.
*
* @author Middleware Services
*/
public class Base64Codec implements Codec
{
/** Encoder. */
private final Encoder encoder;
/** Decoder. */
private final Decoder decoder;
/** Custom alphabet to use. */
private final String customAlphabet;
/** Whether input/output padding is supported. */
private final boolean padding;
/**
* Creates a new instance using the base-64 alphabet defined in RFC 4648.
*/
public Base64Codec()
{
encoder = new Base64Encoder();
decoder = new Base64Decoder();
customAlphabet = null;
padding = true;
}
/**
* Creates a new instance using the given 64-character alphabet.
*
* @param alphabet 64-character alphabet to use.
*/
public Base64Codec(final String alphabet)
{
this(alphabet, true);
}
/**
* Creates a new instance using the given 64-character alphabet with option to enable/disable padding.
*
* @param alphabet 64-character alphabet to use.
* @param inputOutputPadding True to enable support for padding, false otherwise.
*/
public Base64Codec(final String alphabet, final boolean inputOutputPadding)
{
customAlphabet = alphabet;
padding = inputOutputPadding;
encoder = newEncoder();
decoder = newDecoder();
}
@Override
public Encoder getEncoder()
{
return encoder;
}
@Override
public Decoder getDecoder()
{
return decoder;
}
@Override
public Encoder newEncoder()
{
final Base64Encoder encoder;
if (customAlphabet != null) {
encoder = new Base64Encoder(customAlphabet);
} else {
encoder = new Base64Encoder();
}
encoder.setPaddedOutput(padding);
return encoder;
}
@Override
public Decoder newDecoder()
{
final Base64Decoder decoder;
if (customAlphabet != null) {
decoder = new Base64Decoder(customAlphabet);
} else {
decoder = new Base64Decoder();
}
decoder.setPaddedInput(padding);
return decoder;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/Base64Decoder.java 0000664 0000000 0000000 00000006124 14226043123 0026056 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
/**
* Stateful base 64 decoder with support for line breaks.
*
* @author Middleware Services
*/
public class Base64Decoder extends AbstractBaseNDecoder
{
/** Default base-64 character decoding table. */
private static final byte[] DEFAULT_DECODING_TABLE;
/** URL and filesystem-safe base-64 character decoding table. */
private static final byte[] URLSAFE_DECODING_TABLE;
/* Initializes the character decoding table. */
static
{
DEFAULT_DECODING_TABLE = decodingTable("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
URLSAFE_DECODING_TABLE = decodingTable("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", 64);
}
/** Creates a new instance that decodes base 64-encoded input in the default character set. */
public Base64Decoder()
{
this(false);
}
/**
* Creates a new instance that decodes base 64-encoded input in the optional URL-safe character set.
*
* @param urlSafe True to use URL and filesystem-safe character set, false otherwise.
*/
public Base64Decoder(final boolean urlSafe)
{
super(urlSafe ? URLSAFE_DECODING_TABLE : DEFAULT_DECODING_TABLE);
}
/**
* Creates a new instance that decodes base-64 character data encoded in the given alphabet.
*
* @param alphabet Base-64 alphabet to use for decoding
*/
public Base64Decoder(final String alphabet)
{
super(decodingTable(alphabet, 64));
}
@Override
protected int getBlockLength()
{
return 24;
}
@Override
protected int getBitsPerChar()
{
return 6;
}
/**
* Builder for base-64 decoders.
*/
public static class Builder
{
/** URL-safe alphabet flag. */
private boolean urlSafe;
/** Arbitrary alphbet. */
private String alphabet;
/** Padding flag. */
private boolean padding;
/**
* Sets the URL-safe alphabet flag.
*
* @param safe True for URL-safe alphabet, false otherwise.
*
* @return This instance.
*/
public Builder setUrlSafe(final boolean safe)
{
urlSafe = safe;
return this;
}
/**
* Sets an arbitrary 64-character alphabet for decoding.
*
* @param alpha Alternative alphabet.
*
* @return This instance.
*/
public Builder setAlphabet(final String alpha)
{
alphabet = alpha;
return this;
}
/**
* Sets padding flag on the decoder.
*
* @param pad True for base-64 padding, false otherwise.
*
* @return This instance.
*/
public Builder setPadding(final boolean pad)
{
padding = pad;
return this;
}
/**
* Builds a base-64 decoder with the given options.
*
* @return New base-64 decoder instance.
*/
public Base64Decoder build()
{
final Base64Decoder decoder;
if (alphabet != null) {
decoder = new Base64Decoder(alphabet);
} else {
decoder = new Base64Decoder(urlSafe);
}
decoder.setPaddedInput(padding);
return decoder;
}
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/Base64Encoder.java 0000664 0000000 0000000 00000012073 14226043123 0026070 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
/**
* Stateful base 64 encoder with support for configurable line breaks.
*
* @author Middleware Services
*/
public class Base64Encoder extends AbstractBaseNEncoder
{
/** Default base 64 character encoding table. */
private static final char[] DEFAULT_ENCODING_TABLE;
/** Filesystem and URL-safe base 64 character encoding table. */
private static final char[] URLSAFE_ENCODING_TABLE;
/* Initializes the default character encoding tables. */
static
{
DEFAULT_ENCODING_TABLE = encodingTable("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
URLSAFE_ENCODING_TABLE = encodingTable("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", 64);
}
/** Creates a new instance that produces base 64-encoded output with no line breaks in the default character set. */
public Base64Encoder()
{
// Default to no line breaks.
this(-1);
}
/**
* Creates a new instance that produces base 64-encoded output with no line breaks and optional URL-safe character
* set.
*
* @param urlSafe True to use URL and filesystem-safe character set, false otherwise.
*/
public Base64Encoder(final boolean urlSafe)
{
this(urlSafe, -1);
}
/**
* Creates a new instance that produces base 64-encoded output with the given number of characters per line in the
* default character set.
*
* @param charactersPerLine Number of characters per line. A zero or negative value disables line breaks.
*/
public Base64Encoder(final int charactersPerLine)
{
this(false, charactersPerLine);
}
/**
* Creates a new instance that produces base 64-encoded output with the given number of characters per line with the
* option of URL-safe character set.
*
* @param urlSafe True to use URL and filesystem-safe character set, false otherwise.
* @param charactersPerLine Number of characters per line. A zero or negative value disables line breaks.
*/
public Base64Encoder(final boolean urlSafe, final int charactersPerLine)
{
super(urlSafe ? URLSAFE_ENCODING_TABLE : DEFAULT_ENCODING_TABLE, charactersPerLine);
}
/**
* Creates a new instance that produces base 64-encoded output with the given 64-character alphabet.
*
* @param alphabet 64-character alphabet to use.
*/
public Base64Encoder(final String alphabet)
{
this(alphabet, -1);
}
/**
* Creates a new instance that produces base 64-encoded output with the given 64-character alphabet with line
* wrapping at the specified line length;
*
* @param alphabet 64-character alphabet to use.
* @param charactersPerLine Number of characters per line. A zero or negative value disables line breaks.
*/
public Base64Encoder(final String alphabet, final int charactersPerLine)
{
super(encodingTable(alphabet, 64), charactersPerLine);
}
@Override
protected int getBlockLength()
{
return 24;
}
@Override
protected int getBitsPerChar()
{
return 6;
}
/**
* Builder for base-64 encoders.
*/
public static class Builder
{
/** URL-safe alphabet flag. */
private boolean urlSafe;
/** Arbitrary alphbet. */
private String alphabet;
/** Padding flag. */
private boolean padding;
/** Number of base-64 characters per line in output. */
private int charactersPerLine = -1;
/**
* Sets the URL-safe alphabet flag.
*
* @param safe True for URL-safe alphabet, false otherwise.
*
* @return This instance.
*/
public Base64Encoder.Builder setUrlSafe(final boolean safe)
{
urlSafe = safe;
return this;
}
/**
* Sets an arbitrary 64-character alphabet for encoding.
*
* @param alpha Alternative alphabet.
*
* @return This instance.
*/
public Base64Encoder.Builder setAlphabet(final String alpha)
{
alphabet = alpha;
return this;
}
/**
* Sets padding flag on the encoder.
*
* @param pad True for base-64 padding, false otherwise.
*
* @return This instance.
*/
public Base64Encoder.Builder setPadding(final boolean pad)
{
padding = pad;
return this;
}
/**
* Sets the number of characters per line in output produced by the encoder.
*
* @param lineLength Number of characters per line. Set to -1
to suppress line breaks.
*
* @return This instance.
*/
public Base64Encoder.Builder setCharactersPerLine(final int lineLength)
{
charactersPerLine = lineLength;
return this;
}
/**
* Builds a base-64 encoder with the given options.
*
* @return New base-64 encoder instance.
*/
public Base64Encoder build()
{
final Base64Encoder decoder;
if (alphabet != null) {
decoder = new Base64Encoder(alphabet, charactersPerLine);
} else {
decoder = new Base64Encoder(urlSafe, charactersPerLine);
}
decoder.setPaddedOutput(padding);
return decoder;
}
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/Codec.java 0000664 0000000 0000000 00000001116 14226043123 0024555 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
/**
* Container for an encoder/decoder pair.
*
* @author Middleware Services
*/
public interface Codec
{
/** @return The byte-to-char encoder of the codec pair. */
Encoder getEncoder();
/** @return The char-to-byte decoder of the codec pair. */
Decoder getDecoder();
/** @return A new instance of the byte-to-char encoder of the codec pair. */
Encoder newEncoder();
/** @return A new instance of the char-to-byte decoder of the codec pair. */
Decoder newDecoder();
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/Decoder.java 0000664 0000000 0000000 00000002513 14226043123 0025107 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.EncodingException;
/**
* Describes a potentially stateful character-to-byte decoder.
*
* @author Middleware Services
*/
public interface Decoder
{
/**
* Decodes characters in input buffer into bytes placed in the output buffer. This method may be called multiple
* times, followed by {@link #finalize(ByteBuffer)}. after all input bytes have been provided.
*
* @param input Input character buffer.
* @param output Output byte buffer.
*
* @throws EncodingException on decoding errors.
*/
void decode(CharBuffer input, ByteBuffer output) throws EncodingException;
/**
* Performs final output decoding (e.g. padding) after all input characters have been provided.
*
* @param output Output byte buffer.
*
* @throws EncodingException on decoding errors.
*/
void finalize(ByteBuffer output) throws EncodingException;
/**
* Expected number of bytes in the output buffer for an input buffer of the given size.
*
* @param inputSize Size of input buffer in characters.
*
* @return Minimum byte buffer size required to store all decoded characters in input buffer.
*/
int outputSize(int inputSize);
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/Encoder.java 0000664 0000000 0000000 00000002511 14226043123 0025117 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.EncodingException;
/**
* Describes a potentially stateful byte-to-character encoder.
*
* @author Middleware Services
*/
public interface Encoder
{
/**
* Encodes bytes in input buffer into characters placed in the output buffer. This method may be called multiple
* times, followed by {@link #finalize(java.nio.CharBuffer)} after all input bytes have been provided.
*
* @param input Input byte buffer.
* @param output Output character buffer.
*
* @throws EncodingException on encoding errors.
*/
void encode(ByteBuffer input, CharBuffer output) throws EncodingException;
/**
* Performs final output encoding (e.g. padding) after all input bytes have been provided.
*
* @param output Output character buffer.
*
* @throws EncodingException on encoding errors.
*/
void finalize(CharBuffer output) throws EncodingException;
/**
* Expected number of characters in the output buffer for an input buffer of the given size.
*
* @param inputSize Size of input buffer in bytes.
*
* @return Minimum character buffer size required to store all encoded input bytes.
*/
int outputSize(int inputSize);
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/HexCodec.java 0000664 0000000 0000000 00000002416 14226043123 0025226 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
/**
* Hexadecimal encoder/decoder pair.
*
* @author Middleware Services
*/
public class HexCodec implements Codec
{
/** Encoder. */
private final Encoder encoder;
/** Decoder. */
private final Decoder decoder = new HexDecoder();
/** True to encode in uppercase characters, false otherwise. */
private final boolean uppercase;
/**
* Creates a new instance that outputs lowercase hex characters and supports decoding in either case.
*/
public HexCodec()
{
this(false);
}
/**
* Creates a new instance that optionally outputs uppercase hex characters and supports decoding in either case.
*
* @param uppercaseOutput True to output uppercase alphabetic characters, false for lowercase.
*/
public HexCodec(final boolean uppercaseOutput)
{
uppercase = uppercaseOutput;
encoder = new HexEncoder(false, uppercase);
}
@Override
public Encoder getEncoder()
{
return encoder;
}
@Override
public Decoder getDecoder()
{
return decoder;
}
@Override
public Encoder newEncoder()
{
return new HexEncoder(false, uppercase);
}
@Override
public Decoder newDecoder()
{
return new HexDecoder();
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/HexDecoder.java 0000664 0000000 0000000 00000003777 14226043123 0025571 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Arrays;
import org.cryptacular.EncodingException;
/**
* Stateful hexadecimal character-to-byte decoder.
*
* @author Middleware Services
*/
public class HexDecoder implements Decoder
{
/** Hex character decoding table. */
private static final byte[] DECODING_TABLE = new byte[128];
/* Initializes the character decoding table. */
static {
Arrays.fill(DECODING_TABLE, (byte) -1);
for (int i = 0; i < 10; i++) {
DECODING_TABLE[i + 48] = (byte) i;
}
for (int i = 0; i < 6; i++) {
DECODING_TABLE[i + 65] = (byte) (10 + i);
DECODING_TABLE[i + 97] = (byte) (10 + i);
}
}
/** Number of encoded characters processed. */
private int count;
@Override
public void decode(final CharBuffer input, final ByteBuffer output) throws EncodingException
{
// Ignore leading 0x characters if present
if (input.get(0) == '0' && input.get(1) == 'x') {
input.position(input.position() + 2);
}
byte hi = 0;
byte lo;
char current;
while (input.hasRemaining()) {
current = input.get();
if (current == ':' || Character.isWhitespace(current)) {
continue;
}
if ((count++ & 0x01) == 0) {
hi = lookup(current);
} else {
lo = lookup(current);
output.put((byte) ((hi << 4) | lo));
}
}
}
@Override
public void finalize(final ByteBuffer output) throws EncodingException
{
count = 0;
}
@Override
public int outputSize(final int inputSize)
{
return inputSize / 2;
}
/**
* Looks up the byte that corresponds to the given character.
*
* @param c Encoded character.
*
* @return Decoded byte.
*/
private static byte lookup(final char c)
{
final byte b = DECODING_TABLE[c & 0x7F];
if (b < 0) {
throw new EncodingException("Invalid hex character " + c);
}
return b;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/codec/HexEncoder.java 0000664 0000000 0000000 00000005345 14226043123 0025574 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.EncodingException;
/**
* Stateless hexadecimal byte-to-character encoder.
*
* @author Middleware Services
*/
public class HexEncoder implements Encoder
{
/** Lowercase hex character encoding table. */
private static final char[] LC_ENCODING_TABLE = new char[16];
/** Uppercase hex character encoding table. */
private static final char[] UC_ENCODING_TABLE = new char[16];
/* Initializes the encoding character table. */
static {
initTable("0123456789abcdef", LC_ENCODING_TABLE);
initTable("0123456789ABCDEF", UC_ENCODING_TABLE);
}
/** Flag indicating whether to delimit every two characters with ':' as in key fingerprints, etc. */
private final boolean delimit;
/** Encoding table to use. */
private final char[] table;
/** Creates a new instance that does not delimit bytes in the output hex string. */
public HexEncoder()
{
this(false, false);
}
/**
* Creates a new instance with optional colon-delimiting of bytes.
*
* @param delimitBytes True to delimit every two characters (i.e. every byte) with ':' character.
*/
public HexEncoder(final boolean delimitBytes)
{
this(delimitBytes, false);
}
/**
* Creates a new instance with optional colon-delimiting of bytes and uppercase output.
*
* @param delimitBytes True to delimit every two characters (i.e. every byte) with ':' character.
* @param uppercase True to output uppercase alphabetic characters, false for lowercase.
*/
public HexEncoder(final boolean delimitBytes, final boolean uppercase)
{
delimit = delimitBytes;
table = uppercase ? UC_ENCODING_TABLE : LC_ENCODING_TABLE;
}
@Override
public void encode(final ByteBuffer input, final CharBuffer output) throws EncodingException
{
byte current;
while (input.hasRemaining()) {
current = input.get();
output.put(table[(current & 0xf0) >> 4]);
output.put(table[current & 0x0f]);
if (delimit && input.hasRemaining()) {
output.put(':');
}
}
}
@Override
public void finalize(final CharBuffer output) throws EncodingException {}
@Override
public int outputSize(final int inputSize)
{
int size = inputSize * 2;
if (delimit) {
size += inputSize - 1;
}
return size;
}
/**
* Initializes the encoding table for the given character set.
*
* @param charset Character set.
* @param table Encoding table.
*/
private static void initTable(final String charset, final char[] table)
{
for (int i = 0; i < charset.length(); i++) {
table[i] = charset.charAt(i);
}
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/generator/ 0000775 0000000 0000000 00000000000 14226043123 0023607 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/main/java/org/cryptacular/generator/AbstractOTPGenerator.java 0000664 0000000 0000000 00000004715 14226043123 0030456 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.cryptacular.util.ByteUtil;
/**
* Abstract base class for HOTP and TOTP OTP generation schemes.
*
* @author Middleware Services
*/
public abstract class AbstractOTPGenerator
{
/** Array of modulus values indexed per number of digits in OTP output. */
private static final int[] MODULUS = new int[] {
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
};
/** Number of digits in generated OTP. */
private int numberOfDigits = 6;
/** @return Number of digits in generated OTP. */
public int getNumberOfDigits()
{
return numberOfDigits;
}
/**
* Sets the numbers in the generated OTP.
*
* @param digits Number of digits in generated OTP. MUST be in the range 6 - 9. Default is 6.
*/
public void setNumberOfDigits(final int digits)
{
if (digits < 6 || digits > 9) {
throw new IllegalArgumentException("Number of generated digits must be in range 6-9.");
}
this.numberOfDigits = digits;
}
/**
* Internal OTP generation method.
*
* @param key Per-user key.
* @param count Counter moving factor.
*
* @return Integer OTP.
*/
protected int generateInternal(final byte[] key, final long count)
{
final HMac hmac = new HMac(getDigest());
final byte[] output = new byte[hmac.getMacSize()];
hmac.init(new KeyParameter(key));
hmac.update(ByteUtil.toBytes(count), 0, 8);
hmac.doFinal(output, 0);
return truncate(output) % MODULUS[numberOfDigits];
}
/** @return Digest algorithm used for HMAC operation. */
protected abstract Digest getDigest();
/**
* Truncates HMAC output onto an unsigned (i.e. 31-bit) integer using the strategy discussed in RFC 4226,
* section 5.3.
*
* @param hmac HMAC output.
*
* @return Truncated output.
*/
private int truncate(final byte[] hmac)
{
// Offset is the lowest 4 bits of the computed hash
final int offset = hmac[hmac.length - 1] & 0xf;
return
(hmac[offset] & 0x7f) << 24 |
(hmac[offset + 1] & 0xff) << 16 |
(hmac[offset + 2] & 0xff) << 8 |
(hmac[offset + 3] & 0xff);
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/generator/HOTPGenerator.java 0000664 0000000 0000000 00000001444 14226043123 0027076 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
/**
* OTP generator component that implements the HOTP scheme described in
* RFC 4226.
*
* @author Middleware Services
*/
public class HOTPGenerator extends AbstractOTPGenerator
{
/**
* Generates the OTP given a per-user key and invocation count.
*
* @param key Per-user key.
* @param count Counter moving factor.
*
* @return Integer OTP.
*/
public int generate(final byte[] key, final long count)
{
return generateInternal(key, count);
}
@Override
protected Digest getDigest()
{
return new SHA1Digest();
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/generator/IdGenerator.java 0000664 0000000 0000000 00000000505 14226043123 0026655 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
/**
* Generation strategy for random identifiers.
*
* @author Middleware Services
*/
public interface IdGenerator
{
/**
* Generates a random identifier.
*
* @return Random identifier.
*/
String generate();
}
cryptacular-1.2.5/src/main/java/org/cryptacular/generator/KeyPairGenerator.java 0000664 0000000 0000000 00000005712 14226043123 0027672 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.SecureRandom;
import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
/**
* Static factory that generates various types of asymmetric key pairs.
*
* @author Middleware Services
*/
public final class KeyPairGenerator
{
/** Private constructor of static factory. */
private KeyPairGenerator() {}
/**
* Generates a DSA key pair.
*
* @param random Random source required for key generation.
* @param bitLength Desired key size in bits.
*
* @return DSA key pair of desired size.
*/
public static KeyPair generateDSA(final SecureRandom random, final int bitLength)
{
final org.bouncycastle.jcajce.provider.asymmetric.dsa.KeyPairGeneratorSpi generator =
new org.bouncycastle.jcajce.provider.asymmetric.dsa.KeyPairGeneratorSpi();
generator.initialize(bitLength, random);
return generator.generateKeyPair();
}
/**
* Generates a RSA key pair.
*
* @param random Random source required for key generation.
* @param bitLength Desired key size in bits.
*
* @return RSA key pair of desired size.
*/
public static KeyPair generateRSA(final SecureRandom random, final int bitLength)
{
final org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyPairGeneratorSpi generator =
new org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyPairGeneratorSpi();
generator.initialize(bitLength, random);
return generator.generateKeyPair();
}
/**
* Generates a EC key pair.
*
* @param random Random source required for key generation.
* @param bitLength Desired key size in bits.
*
* @return EC key pair of desired size.
*/
public static KeyPair generateEC(final SecureRandom random, final int bitLength)
{
final org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC generator =
new org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC();
generator.initialize(bitLength, random);
return generator.generateKeyPair();
}
/**
* Generates a EC key pair.
*
* @param random Random source required for key generation.
* @param namedCurve Well-known elliptic curve name that includes domain parameters including key size.
*
* @return EC key pair according to named curve.
*/
public static KeyPair generateEC(final SecureRandom random, final String namedCurve)
{
final org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC generator =
new org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC();
try {
generator.initialize(new ECNamedCurveGenParameterSpec(namedCurve), random);
} catch (InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException("Invalid EC curve " + namedCurve, e);
}
return generator.generateKeyPair();
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/generator/LimitException.java 0000664 0000000 0000000 00000001043 14226043123 0027405 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
/**
* Runtime exception that describes a condition where some fundamental limit imposed by the implementation or
* specification of a generator has been exceeded.
*
* @author Middleware Services
*/
public class LimitException extends RuntimeException
{
/**
* Creates a new instance with the given error description..
*
* @param message Error message.
*/
public LimitException(final String message)
{
super(message);
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/generator/Nonce.java 0000664 0000000 0000000 00000000775 14226043123 0025525 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
/**
* Nonce generation strategy.
*
* @author Middleware Services
*/
public interface Nonce
{
/**
* Generates a nonce value.
*
* @return Nonce bytes.
*
* @throws LimitException When a limit imposed by the nonce generation strategy, if any, is exceeded.
*/
byte[] generate()
throws LimitException;
/** @return Length in bytes of generated nonce values. */
int getLength();
}
cryptacular-1.2.5/src/main/java/org/cryptacular/generator/RandomIdGenerator.java 0000664 0000000 0000000 00000003736 14226043123 0030027 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
import java.security.SecureRandom;
/**
* Generates random identifiers with an alphanumeric character set by default.
*
* @author Middleware Services
*/
public class RandomIdGenerator implements IdGenerator
{
/** Default character set. */
public static final String DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
/** Size of generated identifiers. */
private final int length;
/** Identifier character set. */
private final String charset;
/** Source of randomness. */
private final SecureRandom secureRandom;
/**
* Creates a new instance with the default character set.
*
* @param length Number of characters in generated identifiers.
*/
public RandomIdGenerator(final int length)
{
this(length, DEFAULT_CHARSET);
}
/**
* Creates a new instance with a defined character set.
*
* @param length Number of characters in generated identifiers.
* @param charset Character set.
*/
public RandomIdGenerator(final int length, final String charset)
{
if (length < 1) {
throw new IllegalArgumentException("Length must be positive");
}
this.length = length;
if (charset == null || charset.length() < 2 || charset.length() > 128) {
throw new IllegalArgumentException("Charset length must be in the range 2 - 128");
}
this.charset = charset;
secureRandom = new SecureRandom();
// Call nextBytes to force seeding via default process
secureRandom.nextBytes(new byte[1]);
}
@Override
public String generate()
{
final StringBuilder id = new StringBuilder(length);
final byte[] output = new byte[length];
secureRandom.nextBytes(output);
int index;
for (int i = 0; i < output.length && id.length() < length; i++) {
index = 0x7F & output[i];
id.append(charset.charAt(index % charset.length()));
}
return id.toString();
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/generator/SecretKeyGenerator.java 0000664 0000000 0000000 00000004134 14226043123 0030221 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
import java.security.SecureRandom;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.prng.SP800SecureRandomBuilder;
import org.cryptacular.util.NonceUtil;
/**
* Factory class with static methods for generating {@link SecretKey}s.
*
* @author Middleware Services
*/
public final class SecretKeyGenerator
{
/** Private constructor of static class. */
private SecretKeyGenerator() {}
/**
* Generates a symmetric encryption key whose size is equal to the cipher block size.
*
* @param cipher Cipher with with key will be used.
*
* @return Symmetric encryption key.
*/
public static SecretKey generate(final BlockCipher cipher)
{
return generate(cipher.getBlockSize() * 8, cipher);
}
/**
* Generates a symmetric encryption key of the given length.
*
* @param bitLength Desired key length in bits.
* @param cipher Cipher with with key will be used.
*
* @return Symmetric encryption key.
*/
public static SecretKey generate(final int bitLength, final BlockCipher cipher)
{
// Want as much nonce data as key bits
final byte[] nonce = NonceUtil.randomNonce((bitLength + 7) / 8);
return generate(bitLength, cipher, new SP800SecureRandomBuilder().buildHash(new SHA256Digest(), nonce, false));
}
/**
* Generates a symmetric encryption key of the given length.
*
* @param bitLength Desired key length in bits.
* @param cipher Cipher with with key will be used.
* @param random Randomness provider for key generation.
*
* @return Symmetric encryption key.
*/
public static SecretKey generate(final int bitLength, final BlockCipher cipher, final SecureRandom random)
{
// Round up for bit lengths that are not a multiple of 8
final byte[] key = new byte[(bitLength + 7) / 8];
random.nextBytes(key);
return new SecretKeySpec(key, cipher.getAlgorithmName());
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/generator/TOTPGenerator.java 0000664 0000000 0000000 00000007032 14226043123 0027111 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
import org.bouncycastle.crypto.Digest;
import org.cryptacular.spec.DigestSpec;
import org.cryptacular.spec.Spec;
/**
* OTP generator component that implements the TOTP scheme described in
* RFC 6238.
*
* @author Middleware Services
*/
public class TOTPGenerator extends AbstractOTPGenerator
{
/** Digest algorithm specification. */
private SpecA common use case for this component is creation of IVs for ciphers with 16-byte block size, e.g. AES.
* *Instances of this class are thread safe.
* * @author Middleware Services */ public class BigIntegerCounterNonce implements Nonce { /** Counter. */ private BigInteger counter; /** Length of generated counter nonce values in bytes. */ private final int length; /** * Creates a new instance with given parameters. * * @param counter Initial counter value. * @param length Maximum length of generated counter values in bytes. */ public BigIntegerCounterNonce(final BigInteger counter, final int length) { if (length < 1) { throw new IllegalArgumentException("Length must be positive"); } this.length = length; this.counter = counter; } @Override public byte[] generate() throws LimitException { final byte[] value; synchronized (this) { counter = counter.add(BigInteger.ONE); value = counter.toByteArray(); } if (value.length > length) { throw new LimitException("Counter value exceeded max byte length " + length); } if (value.length < length) { final byte[] temp = new byte[length]; Arrays.fill(temp, (byte) 0); System.arraycopy(value, 0, temp, temp.length - value.length, value.length); return temp; } return value; } @Override public int getLength() { return length; } } cryptacular-1.2.5/src/main/java/org/cryptacular/generator/sp80038a/EncryptedNonce.java 0000664 0000000 0000000 00000003533 14226043123 0030564 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.generator.sp80038a; import javax.crypto.SecretKey; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.cryptacular.generator.LimitException; import org.cryptacular.generator.Nonce; import org.cryptacular.spec.Spec; import org.cryptacular.util.NonceUtil; /** * Nonce generation strategy that produces a random value according to NIST * SP-800-38a, appendix C, method 1 (encrypted nonce), suitable for use with any block cipher mode described in that * standard except OFB. * *Instances of this class are thread safe.
* * @author Middleware Services */ public class EncryptedNonce implements Nonce { /** Block cipher. */ private final BlockCipher cipher; /** Encryption key. */ private final SecretKey key; /** * Creates a new instance. * * @param cipherSpec Block cipher specification. * @param key Symmetric key. */ public EncryptedNonce(final SpecInstances of this class are thread safe.
* * @author Middleware Services * @see BigIntegerCounterNonce */ public class LongCounterNonce implements Nonce { /** Counter. */ private final AtomicLong counter; /** Creates a new instance whose counter values start at 1. */ public LongCounterNonce() { this(0); } /** * Creates a new instance whose counter values start above the given value. * * @param start Start value. */ public LongCounterNonce(final long start) { counter = new AtomicLong(start); } @Override public byte[] generate() throws LimitException { return ByteUtil.toBytes(counter.incrementAndGet()); } @Override public int getLength() { return 8; } } cryptacular-1.2.5/src/main/java/org/cryptacular/generator/sp80038a/RBGNonce.java 0000664 0000000 0000000 00000003056 14226043123 0027241 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.generator.sp80038a; import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; import org.cryptacular.generator.LimitException; import org.cryptacular.generator.Nonce; import org.cryptacular.util.NonceUtil; /** * Nonce generation strategy that produces a random value according to NIST * SP-800-38a, appendix C, method 2 (random number generator), suitable for use with any block cipher mode described * in that standard except OFB. * *Instances of this class are thread safe.
* * @author Middleware Services */ public class RBGNonce implements Nonce { /** Length of generated nonces. */ private final int length; /** Random bit generator. */ private final SP80090DRBG rbg; /** Creates a new instance that produces 16-bytes (128-bits) of random data. */ public RBGNonce() { this(16); } /** * Creates a new instance that produces length bytes of random data. * * @param length Number of bytes in generated nonce values. */ public RBGNonce(final int length) { if (length < 1) { throw new IllegalArgumentException("Length must be positive"); } this.length = length; this.rbg = NonceUtil.newRBG(length); } @Override public byte[] generate() throws LimitException { final byte[] random = new byte[length]; synchronized (rbg) { rbg.generate(random, null, false); } return random; } @Override public int getLength() { return length; } } cryptacular-1.2.5/src/main/java/org/cryptacular/generator/sp80038d/ 0000775 0000000 0000000 00000000000 14226043123 0025000 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/main/java/org/cryptacular/generator/sp80038d/CounterNonce.java 0000664 0000000 0000000 00000010721 14226043123 0030246 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.generator.sp80038d; import java.util.concurrent.atomic.AtomicLong; import org.cryptacular.generator.LimitException; import org.cryptacular.generator.Nonce; import org.cryptacular.util.ByteUtil; /** * Deterministic nonce generation strategy that uses a counter for the invocation field as described in NIST SP-800-38D, section 8.2.1. The * invocation part of the sequence is always 64 bits (8 bytes) due to the use of along
, thus the length of
* the nonce is determined by the length of the fixed part: length = 8 + fixed.length
.
*
* NOTE: users of this class are responsible for maintaining the invocation count in order to * support enforcement of constraints described in section 8.3; namely the following:
* *The total number of invocations of the authenticated encryption function shall not exceed 232, * including all IV lengths and all instances of the authenticated encryption function with the given key.* *
Instances of this class enforce this constraint by considering the nonce length, which determines whether the * constraint applies, and the invocation count. The invocation count is incremented upon every invocation of {@link * #generate()} method. The current invocation count is accessible via {@link #getInvocations()}.
* *Instances of this class are thread safe.
* * @author Middleware Services */ public class CounterNonce implements Nonce { /** Default nonce getLength is {@value} bytes. */ public static final int DEFAULT_LENGTH = 12; /** * Maximum invocations is 232. Does not apply to nonces with default getLength, {@value #DEFAULT_LENGTH}. */ public static final long MAX_INVOCATIONS = 0xFFFFFFFFL; /** Fixed field value. */ private final byte[] fixed; /** Invocation count. */ private final AtomicLong count; /** * Creates a new instance. * * @param fixed User-defined fixed field value. * @param invocations Initial invocation count. The invocations field is incremented _before_ use in {@link * #generate()}. */ public CounterNonce(final String fixed, final long invocations) { this(ByteUtil.toBytes(fixed), invocations); } /** * Creates a new instance. Instances of this method produces nonces of the default length, {@value #DEFAULT_LENGTH}, * and are not subject to constraints on the number of invocations. * * @param fixed User-defined fixed field value. * @param invocations Initial invocation count. The invocations field is incremented _before_ use in {@link * #generate()}. */ public CounterNonce(final int fixed, final long invocations) { this(ByteUtil.toBytes(fixed), invocations); } /** * Creates a new instance. * * @param fixed User-defined fixed field value. * @param invocations Initial invocation count. The invocations field is incremented _before_ use in {@link * #generate()}. */ public CounterNonce(final long fixed, final long invocations) { this(ByteUtil.toBytes(fixed), invocations); } /** * Creates a new instance. * * @param fixed User-defined fixed field value. * @param invocations Initial invocation count. The invocations field is incremented _before_ use in {@link * #generate()}. */ public CounterNonce(final byte[] fixed, final long invocations) { if (fixed == null || fixed.length == 0) { throw new IllegalArgumentException("Fixed part cannot be null or empty."); } this.count = new AtomicLong(invocations); this.fixed = fixed; } @Override public byte[] generate() throws LimitException { final byte[] value = new byte[getLength()]; System.arraycopy(fixed, 0, value, 0, fixed.length); final long next = count.incrementAndGet(); if (value.length != DEFAULT_LENGTH) { // Enforce constraints described in section 8.3 if (next > MAX_INVOCATIONS) { throw new LimitException("Exceeded 2^32 invocations."); } } ByteUtil.toBytes(next, value, fixed.length); return value; } @Override public int getLength() { return fixed.length + 8; } /** @return Current invocation count. */ public long getInvocations() { return count.get(); } } cryptacular-1.2.5/src/main/java/org/cryptacular/generator/sp80038d/RBGNonce.java 0000664 0000000 0000000 00000006666 14226043123 0027256 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.generator.sp80038d; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG; import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; import org.cryptacular.generator.LimitException; import org.cryptacular.generator.Nonce; import org.cryptacular.util.ByteUtil; import org.cryptacular.util.NonceUtil; /** * RBG-based nonce generation strategy that uses a RBG component to produce values for the invocation field as described * in NIST SP-800-38D, section 8.2.2. * *NOTE: users of this class are responsible for counting number of invocations and enforcing the * constraints described in section 8.3; namely the following:
* *The total number of invocations of the authenticated encryption function shall not exceed 232, * including all IV lengths and all instances of the authenticated encryption function with the given key.* *
Instances of this class are thread safe.
* * @author Middleware Services */ public class RBGNonce implements Nonce { /** Fixed field value. */ private final byte[] fixed; /** Number of bytes of random data in invocation field. */ private final int randomLength; /** Random bit generator. */ private final SP80090DRBG rbg; /** * Creates a new instance that produces 12-bytes (96-bits) of random data; that is, the fixed field of the nonce is * null. */ public RBGNonce() { this(12); } /** * Creates a new instance that produces length bytes of random data; that is, the fixed field of the nonce is null. * * @param randomLength Number of bytes in the random part of the nonce. MUST be at least 12. */ public RBGNonce(final int randomLength) { this(null, randomLength); } /** * Creates a new instance using the given fixed field value. * * @param fixed User-defined fixed field value. * @param randomLength Number of bytes in the random part of the nonce. MUST be at least 12. */ public RBGNonce(final String fixed, final int randomLength) { if (randomLength < 12) { throw new IllegalArgumentException("Must specify at least 12 bytes (96 bits) for random part."); } this.randomLength = randomLength; if (fixed != null) { this.fixed = ByteUtil.toBytes(fixed); } else { this.fixed = new byte[0]; } this.rbg = newRBG(this.randomLength, this.fixed); } @Override public byte[] generate() throws LimitException { final byte[] random = new byte[randomLength]; synchronized (rbg) { rbg.generate(random, null, false); } final byte[] value = new byte[getLength()]; System.arraycopy(fixed, 0, value, 0, fixed.length); System.arraycopy(random, 0, value, fixed.length, random.length); return value; } @Override public int getLength() { return fixed.length + randomLength; } /** * Creates a new DRBG instance. * * @param length Length in bits of values produced by DRBG. * @param domain Domain qualifier. * * @return New DRBG instance. */ private static SP80090DRBG newRBG(final int length, final byte[] domain) { return new HashSP800DRBG( new SHA256Digest(), length, NonceUtil.randomEntropySource(length), domain, NonceUtil.timestampNonce(8)); } } cryptacular-1.2.5/src/main/java/org/cryptacular/io/ 0000775 0000000 0000000 00000000000 14226043123 0022230 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/main/java/org/cryptacular/io/ChunkHandler.java 0000664 0000000 0000000 00000001455 14226043123 0025446 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.io; import java.io.IOException; import java.io.OutputStream; /** * Callback interface that supports arbitrary processing of data chunks read from an input stream. * * @author Middleware Services */ public interface ChunkHandler { /** * Processes the given chunk of data and writes it to the output stream. * * @param input Chunk of input data to process. * @param offset Offset into input array where data to process starts. * @param count Number of bytes of input data to process. * @param output Output stream where processed data is written. * * @throws IOException On IO errors. */ void handle(byte[] input, int offset, int count, OutputStream output) throws IOException; } cryptacular-1.2.5/src/main/java/org/cryptacular/io/ClassPathResource.java 0000664 0000000 0000000 00000003051 14226043123 0026464 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.io; import java.io.InputStream; /** * Resource that produces a {@link InputStream} from a classpath resource. * * @author Middleware Services */ public class ClassPathResource implements Resource { /** Classpath location of resource. */ private final String classPath; /** Class loader used to get input streams on classpath locations. */ private final ClassLoader classLoader; /** * Creates a new resource that reads from the given classpath location.
* Thread.currentThread().getContextClassLoader()
is used to obtain the class loader used to obtain an input
* stream on the given classpath.
*
* @param path Classpath location.
*/
public ClassPathResource(final String path)
{
this(path, Thread.currentThread().getContextClassLoader());
}
/**
* Creates a new resource that reads from the given classpath location.
*
* @param path Classpath location.
* @param loader Class loader used to obtain an input stream on the given classpath location.
*/
public ClassPathResource(final String path, final ClassLoader loader)
{
// Strip leading / since absolute paths are not supported by
// ClassLoader#getResourceAsStream(...)
if (path.startsWith("/")) {
this.classPath = path.substring(1);
} else {
this.classPath = path;
}
this.classLoader = loader;
}
@Override
public InputStream getInputStream()
{
return classLoader.getResourceAsStream(classPath);
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/io/DecodingInputStream.java 0000664 0000000 0000000 00000006520 14226043123 0027006 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.io;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.codec.Base64Decoder;
import org.cryptacular.codec.Decoder;
import org.cryptacular.codec.HexDecoder;
/**
* Filters read bytes through a {@link Decoder} such that consumers obtain raw (decoded) bytes from read operations.
*
* @author Middleware Services
*/
public class DecodingInputStream extends FilterInputStream
{
/** Performs decoding. */
private final Decoder decoder;
/** Wraps the input stream to convert bytes to characters. */
private final InputStreamReader reader;
/** Holds input bytes as characters. */
private CharBuffer input;
/** Receives decoding result. */
private ByteBuffer output;
/**
* Creates a new instance that wraps the given stream and performs decoding using the given encoder component.
*
* @param in Input stream to wrap.
* @param d Decoder that provides on-the-fly decoding.
*/
public DecodingInputStream(final InputStream in, final Decoder d)
{
super(in);
if (d == null) {
throw new IllegalArgumentException("Decoder cannot be null.");
}
decoder = d;
reader = new InputStreamReader(in);
}
@Override
public int read()
throws IOException
{
return read(new byte[1]);
}
@Override
public int read(final byte[] b)
throws IOException
{
return read(b, 0, b.length);
}
@Override
public int read(final byte[] b, final int off, final int len)
throws IOException
{
prepareInputBuffer(len - off);
prepareOutputBuffer();
if (reader.read(input) < 0) {
decoder.finalize(output);
if (output.position() == 0) {
return -1;
}
} else {
input.flip();
decoder.decode(input, output);
}
output.flip();
output.get(b, off, output.limit());
return output.position();
}
/**
* Creates a new instance that decodes base64 input from the given stream.
*
* @param in Wrapped input stream.
*
* @return Decoding input stream that decodes base64 output.
*/
public static DecodingInputStream base64(final InputStream in)
{
return new DecodingInputStream(in, new Base64Decoder());
}
/**
* Creates a new instance that decodes hexadecimal input from the given stream.
*
* @param in Wrapped input stream.
*
* @return Decoding input stream that decodes hexadecimal output.
*/
public static DecodingInputStream hex(final InputStream in)
{
return new DecodingInputStream(in, new HexDecoder());
}
/**
* Prepares the input buffer to receive the given number of bytes.
*
* @param required Number of bytes required.
*/
private void prepareInputBuffer(final int required)
{
if (input == null || input.capacity() < required) {
input = CharBuffer.allocate(required);
} else {
input.clear();
}
}
/** Prepares the output buffer based on input buffer capacity. */
private void prepareOutputBuffer()
{
final int required = decoder.outputSize(input.capacity());
if (output == null || output.capacity() < required) {
output = ByteBuffer.allocate(required);
} else {
output.clear();
}
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/io/DirectByteArrayOutputStream.java 0000664 0000000 0000000 00000001545 14226043123 0030532 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.io;
import java.io.ByteArrayOutputStream;
/**
* Extends {@link ByteArrayOutputStream} by allowing direct access to the internal byte buffer.
*
* @author Middleware Services
*/
public class DirectByteArrayOutputStream extends ByteArrayOutputStream
{
/** Creates a new instance with a buffer of the default size. */
public DirectByteArrayOutputStream()
{
super();
}
/**
* Creates a new instance with a buffer of the given initial capacity.
*
* @param capacity Initial capacity of internal buffer.
*/
public DirectByteArrayOutputStream(final int capacity)
{
super(capacity);
}
/**
* Gets the internal byte buffer.
*
* @return Internal buffer that holds written bytes.
*/
public byte[] getBuffer()
{
return buf;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/io/EncodingOutputStream.java 0000664 0000000 0000000 00000007432 14226043123 0027224 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.io;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.codec.Base64Encoder;
import org.cryptacular.codec.Encoder;
import org.cryptacular.codec.HexEncoder;
/**
* Filters written bytes through an {@link Encoder} such that encoded data is written to the underlying output stream.
*
* @author Middleware Services
*/
public class EncodingOutputStream extends FilterOutputStream
{
/** Performs decoding. */
private final Encoder encoder;
/** Wraps the output stream to convert characters to bytes. */
private final OutputStreamWriter writer;
/** Receives encoding result. */
private CharBuffer output;
/**
* Creates a new instance that wraps the given stream and performs encoding using the given encoder component.
*
* @param out Output stream to wrap.
* @param e Encoder that provides on-the-fly encoding.
*/
public EncodingOutputStream(final OutputStream out, final Encoder e)
{
super(out);
if (e == null) {
throw new IllegalArgumentException("Encoder cannot be null.");
}
encoder = e;
writer = new OutputStreamWriter(out);
}
@Override
public void write(final int b)
throws IOException
{
write(new byte[] {(byte) b});
}
@Override
public void write(final byte[] b)
throws IOException
{
write(b, 0, b.length);
}
@Override
public void write(final byte[] b, final int off, final int len)
throws IOException
{
final ByteBuffer input = ByteBuffer.wrap(b, off, len);
final int required = encoder.outputSize(len - off);
if (output == null || output.capacity() < required) {
output = CharBuffer.allocate(required);
} else {
output.clear();
}
encoder.encode(input, output);
output.flip();
writer.write(output.toString());
writer.flush();
}
@Override
public void flush()
throws IOException
{
writer.flush();
}
@Override
public void close()
throws IOException
{
if (output == null) {
output = CharBuffer.allocate(8);
} else {
output.clear();
}
encoder.finalize(output);
output.flip();
writer.write(output.toString());
writer.flush();
writer.close();
}
/**
* Creates a new instance that produces base64 output in the given stream.
*
* NOTE: there are no line breaks in the output with this version.
* * @param out Wrapped output stream. * * @return Encoding output stream that produces base64 output. */ public static EncodingOutputStream base64(final OutputStream out) { return base64(out, -1); } /** * Creates a new instance that produces base64 output in the given stream. * *NOTE: this version supports output with configurable line breaks.
* * @param out Wrapped output stream. * @param lineLength Length of each base64-encoded line in output. A zero or negative value disables line breaks. * * @return Encoding output stream that produces base64 output. */ public static EncodingOutputStream base64(final OutputStream out, final int lineLength) { return new EncodingOutputStream(out, new Base64Encoder(lineLength)); } /** * Creates a new instance that produces hexadecimal output in the given stream. * *NOTE: there are no line breaks in the output.
* * @param out Wrapped output stream. * * @return Encoding output stream that produces hexadecimal output. */ public static EncodingOutputStream hex(final OutputStream out) { return new EncodingOutputStream(out, new HexEncoder()); } } cryptacular-1.2.5/src/main/java/org/cryptacular/io/FileResource.java 0000664 0000000 0000000 00000001645 14226043123 0025470 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.io; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * Resource that produces a buffered {@link FileInputStream} from a file. * * @author Middleware Services */ public class FileResource implements Resource { /** Underlying file resource. */ private File file; /** * Creates a new file resource. * * @param file Non-null file. */ public FileResource(final File file) { if (file == null) { throw new IllegalArgumentException("File cannot be null."); } this.file = file; } @Override public InputStream getInputStream() throws IOException { return new BufferedInputStream(new FileInputStream(file)); } @Override public String toString() { return file.toString(); } } cryptacular-1.2.5/src/main/java/org/cryptacular/io/Resource.java 0000664 0000000 0000000 00000001575 14226043123 0024672 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.io; import java.io.IOException; import java.io.InputStream; /** * Resource descriptor that provides a strategy to get an {@link InputStream} to read bytes. * * @author Middleware Services */ public interface Resource { /** * Gets an input stream around the resource. Callers of this method are responsible for resource cleanup; it should be * sufficient to simply call {@link java.io.InputStream#close()} unless otherwise noted. * *Implementers should produce a new instance on every call to this method to provide for thread-safe usage * patterns on a shared resource.
* * @return Input stream around underlying resource, e.g. file, remote resource (URI), etc. * * @throws IOException On IO errors. */ InputStream getInputStream() throws IOException; } cryptacular-1.2.5/src/main/java/org/cryptacular/io/URLResource.java 0000664 0000000 0000000 00000001474 14226043123 0025253 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.io; import java.io.IOException; import java.io.InputStream; import java.net.URL; /** * Describes a (presumably remote) resource accessible via URL. * * @author Middleware Services */ public class URLResource implements Resource { /** Location of resource. */ private URL url; /** * Creates a new URL resource. * * @param url Non-null URL where resource is located. */ public URLResource(final URL url) { if (url == null) { throw new IllegalArgumentException("URL cannot be null."); } this.url = url; } @Override public InputStream getInputStream() throws IOException { return url.openStream(); } @Override public String toString() { return url.toString(); } } cryptacular-1.2.5/src/main/java/org/cryptacular/pbe/ 0000775 0000000 0000000 00000000000 14226043123 0022367 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/main/java/org/cryptacular/pbe/AbstractEncryptionScheme.java 0000664 0000000 0000000 00000006266 14226043123 0030207 0 ustar 00root root 0000000 0000000 /* 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. * *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.
* * @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); } } cryptacular-1.2.5/src/main/java/org/cryptacular/pbe/EncryptionScheme.java 0000664 0000000 0000000 00000003340 14226043123 0026511 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.pbe; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Describes a password-based encryption scheme. * * @author Middleware Services * @version $Revision: 2744 $ */ public interface EncryptionScheme { /** * Encrypts the given plaintext bytes into a byte array of ciphertext using the derived key. * * @param plaintext Input plaintext bytes. * * @return Ciphertext resulting from plaintext encryption. */ byte[] encrypt(byte[] plaintext); /** * Encrypts the data in the given plaintext input stream into ciphertext in the output stream. Use {@link * org.cryptacular.io.EncodingOutputStream} to produce ciphertext bytes that encoded as a string data in the output * stream. * * @param in Input stream of plaintext. * @param out Output stream of ciphertext. * * @throws IOException On stream read/write errors. */ void encrypt(InputStream in, OutputStream out) throws IOException; /** * Decrypts the given ciphertext into plaintext using the derived key. * * @param ciphertext Input ciphertext bytes. * * @return Plaintext resulting from ciphertext decryption. */ byte[] decrypt(byte[] ciphertext); /** * Decrypts ciphertext from an input stream into plaintext in the output stream. Use {@link * org.cryptacular.io.DecodingInputStream} to handle input ciphertext encoded as string data. * * @param in Input stream of ciphertext. * @param out Output stream of plaintext. * * @throws IOException On stream read/write errors. */ void decrypt(InputStream in, OutputStream out) throws IOException; } cryptacular-1.2.5/src/main/java/org/cryptacular/pbe/OpenSSLAlgorithm.java 0000664 0000000 0000000 00000004662 14226043123 0026374 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.pbe; import org.cryptacular.spec.KeyedBlockCipherSpec; /** * Describes block ciphers allowed with the OpenSSL password-based encryption scheme. * * @author Middleware Services */ public enum OpenSSLAlgorithm { /** AES-128 in CBC mode. */ AES_128_CBC("aes-128-cbc", new KeyedBlockCipherSpec("AES", "CBC", "PKCS5", 128)), /** AES-192 in CBC mode. */ AES_192_CBC("aes-192-cbc", new KeyedBlockCipherSpec("AES", "CBC", "PKCS5", 192)), /** AES-256 in CBC mode. */ AES_256_CBC("aes-256-cbc", new KeyedBlockCipherSpec("AES", "CBC", "PKCS5", 256)), /** DES in CBC mode. */ DES_CBC("des-cbc", new KeyedBlockCipherSpec("DES", "CBC", "PKCS5", 64)), /** Triple DES in CBC mode. */ DES_EDE3_CBC("des-ede3-cbc", new KeyedBlockCipherSpec("DESede", "CBC", "PKCS5", 192)), /** 128-bit RC2 in CBC mode. */ RC2_CBC("rc2-cbc", new KeyedBlockCipherSpec("RC2", "CBC", "PKCS5", 128)), /** 40-bit RC2 in CBC mode. */ RC2_40_CBC("rc2-40-cbc", new KeyedBlockCipherSpec("RC2", "CBC", "PKCS5", 40)), /** 64-bit RC2 in CBC mode. */ RC2_64_CBC("rc2-64-cbc", new KeyedBlockCipherSpec("RC2", "CBC", "PKCS5", 64)); /** Algorithm identifier, e.g. aes-128-cbc. */ private final String algorithmId; /** Cipher algorithm specification. */ private final KeyedBlockCipherSpec cipherSpec; /** * Creates a new instance with given parameters. * * @param algId Algorithm identifier, e.g. aes-128-cbc. * @param cipherSpec Block cipher specification that corresponds to algorithm ID. */ OpenSSLAlgorithm(final String algId, final KeyedBlockCipherSpec cipherSpec) { this.algorithmId = algId; this.cipherSpec = cipherSpec; } /** @return OpenSSL algorithm identifier, e.g. aes-128-cbc. */ public String getAlgorithmId() { return algorithmId; } /** @return Cipher algorithm specification. */ public KeyedBlockCipherSpec getCipherSpec() { return cipherSpec; } /** * Converts an OID to the corresponding algorithm specification. * * @param algorithmId Algorithm OID. * * @return Algorithm spec. */ public static OpenSSLAlgorithm fromAlgorithmId(final String algorithmId) { for (OpenSSLAlgorithm alg : values()) { if (alg.getAlgorithmId().equalsIgnoreCase(algorithmId)) { return alg; } } throw new IllegalArgumentException("Unsupported algorithm " + algorithmId); } } cryptacular-1.2.5/src/main/java/org/cryptacular/pbe/OpenSSLEncryptionScheme.java 0000664 0000000 0000000 00000004161 14226043123 0027717 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.pbe; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.PBEParametersGenerator; import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator; import org.bouncycastle.crypto.params.ParametersWithIV; /** * Password-based encryption scheme used by OpenSSL for encrypting private keys. * * @author Middleware Services * @version $Revision: 2744 $ */ public class OpenSSLEncryptionScheme extends AbstractEncryptionScheme { /** * Creates a new instance using the given parameters. * * @param cipher Buffered block cipher algorithm. * @param salt Salt data for key generation function. * @param keyBitLength Size of derived keys in bits. * @param password Password used to derive key. */ public OpenSSLEncryptionScheme( final BufferedBlockCipher cipher, final byte[] salt, final int keyBitLength, final char[] password) { final OpenSSLPBEParametersGenerator generator = new OpenSSLPBEParametersGenerator(); generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password), salt); setCipher(cipher); setCipherParameters(generator.generateDerivedParameters(keyBitLength)); } /** * Creates a new instance from an algorithm and salt data. * * @param algorithm OpenSSL key encryption algorithm. * @param iv Explicit IV; first 8 bytes also used for salt in PBE key generation. * @param password Password used to derive key. */ public OpenSSLEncryptionScheme(final OpenSSLAlgorithm algorithm, final byte[] iv, final char[] password) { byte[] salt = iv; if (iv.length > 8) { salt = new byte[8]; System.arraycopy(iv, 0, salt, 0, 8); } final OpenSSLPBEParametersGenerator generator = new OpenSSLPBEParametersGenerator(); generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password), salt); setCipher(algorithm.getCipherSpec().newInstance()); setCipherParameters( new ParametersWithIV(generator.generateDerivedParameters(algorithm.getCipherSpec().getKeyLength()), iv)); } } cryptacular-1.2.5/src/main/java/org/cryptacular/pbe/PBES1Algorithm.java 0000664 0000000 0000000 00000006067 14226043123 0025724 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.pbe; import org.cryptacular.spec.BufferedBlockCipherSpec; import org.cryptacular.spec.DigestSpec; /** * Password-based encryption algorithms defined in PKCS#5 for PBES1 scheme. * * @author Middleware Services * @version $Revision: 2745 $ */ public enum PBES1Algorithm { /** PBES1 encryption method with MD2 hash and DES CBC cipher. */ PbeWithMD2AndDES_CBC( "1.2.840.113549.1.5.1", new BufferedBlockCipherSpec("DES", "CBC", "PKCS5"), new DigestSpec("MD2")), /** PBES1 encryption method with MD2 hash and RC2 CBC cipher. */ PbeWithMD2AndRC2_CBC( "1.2.840.113549.1.5.4", new BufferedBlockCipherSpec("RC2", "CBC", "PKCS5"), new DigestSpec("MD2")), /** PBES1 encryption method with MD5 hash and DES CBC cipher. */ PbeWithMD5AndDES_CBC( "1.2.840.113549.1.5.3", new BufferedBlockCipherSpec("DES", "CBC", "PKCS5"), new DigestSpec("MD5")), /** PBES1 encryption method with MD5 hash and RC2 CBC cipher. */ PbeWithMD5AndRC2_CBC( "1.2.840.113549.1.5.6", new BufferedBlockCipherSpec("RC2", "CBC", "PKCS5"), new DigestSpec("MD5")), /** PBES1 encryption method with SHA1 hash and DES CBC cipher. */ PbeWithSHA1AndDES_CBC( "1.2.840.113549.1.5.10", new BufferedBlockCipherSpec("DES", "CBC", "PKCS5"), new DigestSpec("SHA1")), /** PBES1 encryption method with SHA1 hash and RC2 CBC cipher. */ PbeWithSHA1AndRC2_CBC( "1.2.840.113549.1.5.11", new BufferedBlockCipherSpec("RC2", "CBC", "PKCS5"), new DigestSpec("SHA1")); /** Algorithm identifier OID. */ private final String oid; /** Cipher algorithm specification. */ private final BufferedBlockCipherSpec cipherSpec; /** Pseudorandom function digest specification. */ private final DigestSpec digestSpec; /** * Creates a new instance with given parameters. * * @param id Algorithm OID. * @param cipherSpec Cipher algorithm specification. * @param digestSpec Digest specification used for pseudorandom function. */ PBES1Algorithm(final String id, final BufferedBlockCipherSpec cipherSpec, final DigestSpec digestSpec) { this.oid = id; this.cipherSpec = cipherSpec; this.digestSpec = digestSpec; } /** * Gets the PBE algorithm for the given object identifier. * * @param oid PBE algorithm OID. * * @return Algorithm whose identifier equals given value. * * @throws IllegalArgumentException If no matching algorithm found. */ public static PBES1Algorithm fromOid(final String oid) { for (PBES1Algorithm a : PBES1Algorithm.values()) { if (a.getOid().equals(oid)) { return a; } } throw new IllegalArgumentException("Unknown PBES1Algorithm for OID " + oid); } /** @return the oid */ public String getOid() { return oid; } /** @return Cipher algorithm specification. */ public BufferedBlockCipherSpec getCipherSpec() { return cipherSpec; } /** @return Digest algorithm. */ public DigestSpec getDigestSpec() { return digestSpec; } } cryptacular-1.2.5/src/main/java/org/cryptacular/pbe/PBES1EncryptionScheme.java 0000664 0000000 0000000 00000002675 14226043123 0027256 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.pbe; import org.bouncycastle.asn1.pkcs.PBEParameter; import org.bouncycastle.crypto.PBEParametersGenerator; import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator; /** * Implements the PBES1 encryption scheme defined in PKCS#5v2. * * @author Middleware Services * @version $Revision: 2744 $ */ public class PBES1EncryptionScheme extends AbstractEncryptionScheme { /** Number of bits in derived key. */ public static final int KEY_LENGTH = 64; /** Number of bits IV. */ public static final int IV_LENGTH = 64; /** * Creates a new instance with the given parameters. * * @param alg Describes hash/algorithm pair suitable for PBES1 scheme. * @param params Key generation function salt and iteration count. * @param password Password used to derive key. */ public PBES1EncryptionScheme(final PBES1Algorithm alg, final PBEParameter params, final char[] password) { final byte[] salt = params.getSalt(); final int iterations = params.getIterationCount().intValue(); final PKCS5S1ParametersGenerator generator = new PKCS5S1ParametersGenerator(alg.getDigestSpec().newInstance()); generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password), salt, iterations); setCipher(alg.getCipherSpec().newInstance()); setCipherParameters(generator.generateDerivedParameters(KEY_LENGTH, IV_LENGTH)); } } cryptacular-1.2.5/src/main/java/org/cryptacular/pbe/PBES2Algorithm.java 0000664 0000000 0000000 00000005352 14226043123 0025721 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.pbe; import org.cryptacular.spec.BufferedBlockCipherSpec; /** * Supported password-based encryption algorithms for PKCS#5 PBES2 encryption scheme. The ciphers mentioned in PKCS#5 * are supported as well as others in common use or of presumed value. * * @author Middleware Services * @version $Revision: 2745 $ */ public enum PBES2Algorithm { /** DES CBC cipher. */ DES("1.3.14.3.2.7", new BufferedBlockCipherSpec("DES", "CBC", "PKCS5"), 64), /** 3-DES CBC cipher. */ DESede("1.2.840.113549.3.7", new BufferedBlockCipherSpec("DESede", "CBC", "PKCS5"), 192), /** RC2 CBC cipher. */ RC2("1.2.840.113549.3.2", new BufferedBlockCipherSpec("RC2", "CBC", "PKCS5"), 64), /** RC5 CBC cipher. */ RC5("1.2.840.113549.3.9", new BufferedBlockCipherSpec("RC5", "CBC", "PKCS5"), 128), /** AES-128 CBC cipher. */ AES128("2.16.840.1.101.3.4.1.2", new BufferedBlockCipherSpec("AES", "CBC", "PKCS5"), 128), /** AES-192 CBC cipher. */ AES192("2.16.840.1.101.3.4.1.22", new BufferedBlockCipherSpec("AES", "CBC", "PKCS5"), 192), /** AES-256 CBC cipher. */ AES256("2.16.840.1.101.3.4.1.42", new BufferedBlockCipherSpec("AES", "CBC", "PKCS5"), 256); /** Algorithm identifier OID. */ private final String oid; /** Cipher algorithm specification. */ private final BufferedBlockCipherSpec cipherSpec; /** Cipher key size in bits. */ private final int keySize; /** * Creates a new instance with given parameters. * * @param id Algorithm OID. * @param cipherSpec Cipher algorithm specification. * @param keySizeBits Size of derived key in bits to be used with cipher. */ PBES2Algorithm(final String id, final BufferedBlockCipherSpec cipherSpec, final int keySizeBits) { this.oid = id; this.cipherSpec = cipherSpec; this.keySize = keySizeBits; } /** * Gets the PBE algorithm for the given object identifier. * * @param oid PBE algorithm OID. * * @return Algorithm whose identifier equals given value. * * @throws IllegalArgumentException If no matching algorithm found. */ public static PBES2Algorithm fromOid(final String oid) { for (PBES2Algorithm a : PBES2Algorithm.values()) { if (a.getOid().equals(oid)) { return a; } } throw new IllegalArgumentException("Unknown PBES1Algorithm for OID " + oid); } /** @return the oid */ public String getOid() { return oid; } /** @return Cipher algorithm specification. */ public BufferedBlockCipherSpec getCipherSpec() { return cipherSpec; } /** @return Size of derived key in bits or -1 if algorithm does not define a key size. */ public int getKeySize() { return keySize; } } cryptacular-1.2.5/src/main/java/org/cryptacular/pbe/PBES2EncryptionScheme.java 0000664 0000000 0000000 00000010070 14226043123 0027243 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.pbe; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.pkcs.PBES2Parameters; import org.bouncycastle.asn1.pkcs.PBKDF2Params; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.PBEParametersGenerator; import org.bouncycastle.crypto.engines.RC532Engine; import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.paddings.PKCS7Padding; import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.crypto.params.RC2Parameters; import org.bouncycastle.crypto.params.RC5Parameters; /** * Implements the PBES2 encryption scheme defined in PKCS#5v2. * * @author Middleware Services * @version $Revision: 2744 $ */ public class PBES2EncryptionScheme extends AbstractEncryptionScheme { /** Size of derived key in bits. */ private int keyLength; /** * Creates a new instance with the given parameters. * * @param params PBES2 parameters describing the key derivation function and encryption scheme. * @param password Password used to derive key. */ public PBES2EncryptionScheme(final PBES2Parameters params, final char[] password) { final PBKDF2Params kdfParams = PBKDF2Params.getInstance(params.getKeyDerivationFunc().getParameters()); final byte[] salt = kdfParams.getSalt(); final int iterations = kdfParams.getIterationCount().intValue(); if (kdfParams.getKeyLength() != null) { keyLength = kdfParams.getKeyLength().intValue() * 8; } final PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(); generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password), salt, iterations); initCipher(generator, params.getEncryptionScheme()); } /** * Initializes the block cipher and sets up its initialization parameters. * * @param generator Derived key generator. * @param scheme PKCS#5 encryption scheme. */ private void initCipher( final PKCS5S2ParametersGenerator generator, final org.bouncycastle.asn1.pkcs.EncryptionScheme scheme) { final PBES2Algorithm alg = PBES2Algorithm.fromOid(scheme.getAlgorithm().getId()); if (keyLength == 0) { keyLength = alg.getKeySize(); } byte[] iv = null; CipherParameters cipherParameters = generator.generateDerivedParameters(keyLength); switch (alg) { case RC2: setCipher(alg.getCipherSpec().newInstance()); final ASN1Sequence rc2Params = ASN1Sequence.getInstance(scheme.getParameters()); if (rc2Params.size() > 1) { cipherParameters = new RC2Parameters( ((KeyParameter) cipherParameters).getKey(), ASN1Integer.getInstance(rc2Params.getObjectAt(0)).getValue().intValue()); iv = ASN1OctetString.getInstance(rc2Params.getObjectAt(0)).getOctets(); } break; case RC5: final ASN1Sequence rc5Params = ASN1Sequence.getInstance(scheme.getParameters()); final int rounds = ASN1Integer.getInstance(rc5Params.getObjectAt(1)).getValue().intValue(); final int blockSize = ASN1Integer.getInstance(rc5Params.getObjectAt(2)).getValue().intValue(); if (blockSize == 32) { setCipher(new PaddedBufferedBlockCipher(new CBCBlockCipher(new RC532Engine()), new PKCS7Padding())); } cipherParameters = new RC5Parameters(((KeyParameter) cipherParameters).getKey(), rounds); if (rc5Params.size() > 3) { iv = ASN1OctetString.getInstance(rc5Params.getObjectAt(3)).getOctets(); } break; default: setCipher(alg.getCipherSpec().newInstance()); iv = ASN1OctetString.getInstance(scheme.getParameters()).getOctets(); } if (iv != null) { cipherParameters = new ParametersWithIV(cipherParameters, iv); } setCipherParameters(cipherParameters); } } cryptacular-1.2.5/src/main/java/org/cryptacular/spec/ 0000775 0000000 0000000 00000000000 14226043123 0022553 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/main/java/org/cryptacular/spec/AEADBlockCipherSpec.java 0000664 0000000 0000000 00000006137 14226043123 0027040 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.spec; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.CCMBlockCipher; import org.bouncycastle.crypto.modes.EAXBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.modes.OCBBlockCipher; /** * Describes an AEAD block cipher in terms of a (algorithm, mode) tuple and provides a facility to create a new instance * of the cipher via the {@link #newInstance()} method. * * @author Middleware Services * @version $Revision: 2744 $ */ public class AEADBlockCipherSpec implements Specalgorithm/mode
. */
public static final Pattern FORMAT = Pattern.compile("(?algorithm/mode
.
*
* @return Buffered block cipher specification instance.
*/
public static AEADBlockCipherSpec parse(final String specification)
{
final Matcher m = FORMAT.matcher(specification);
if (!m.matches()) {
throw new IllegalArgumentException("Invalid specification " + specification);
}
return new AEADBlockCipherSpec(m.group("alg"), m.group("mode"));
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/spec/BlockCipherSpec.java 0000664 0000000 0000000 00000006706 14226043123 0026427 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.spec;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.engines.BlowfishEngine;
import org.bouncycastle.crypto.engines.CAST5Engine;
import org.bouncycastle.crypto.engines.CAST6Engine;
import org.bouncycastle.crypto.engines.CamelliaEngine;
import org.bouncycastle.crypto.engines.DESEngine;
import org.bouncycastle.crypto.engines.DESedeEngine;
import org.bouncycastle.crypto.engines.GOST28147Engine;
import org.bouncycastle.crypto.engines.NoekeonEngine;
import org.bouncycastle.crypto.engines.RC2Engine;
import org.bouncycastle.crypto.engines.RC564Engine;
import org.bouncycastle.crypto.engines.RC6Engine;
import org.bouncycastle.crypto.engines.SEEDEngine;
import org.bouncycastle.crypto.engines.SerpentEngine;
import org.bouncycastle.crypto.engines.SkipjackEngine;
import org.bouncycastle.crypto.engines.TEAEngine;
import org.bouncycastle.crypto.engines.TwofishEngine;
import org.bouncycastle.crypto.engines.XTEAEngine;
/**
* Block cipher specification.
*
* @author Middleware Services
*/
public class BlockCipherSpec implements Specalgorithm/mode/padding
. */
public static final Pattern FORMAT = Pattern.compile("(?algorithm/mode/padding
.
*
* @return Buffered block cipher specification instance.
*/
public static BufferedBlockCipherSpec parse(final String specification)
{
final Matcher m = FORMAT.matcher(specification);
if (!m.matches()) {
throw new IllegalArgumentException("Invalid specification " + specification);
}
return new BufferedBlockCipherSpec(m.group("alg"), m.group("mode"), m.group("padding"));
}
/**
* Gets a instance of block cipher padding from a padding name string.
*
* @param padding Name of padding algorithm.
*
* @return Block cipher padding instance.
*/
private static BlockCipherPadding getPadding(final String padding)
{
final String name;
final int pIndex = padding.indexOf("Padding");
if (pIndex > -1) {
name = padding.substring(0, pIndex);
} else {
name = padding;
}
final BlockCipherPadding blockCipherPadding;
if ("ISO7816d4".equalsIgnoreCase(name) | "ISO7816".equalsIgnoreCase(name)) {
blockCipherPadding = new ISO7816d4Padding();
} else if ("ISO10126".equalsIgnoreCase(name) || "ISO10126-2".equalsIgnoreCase(name)) {
blockCipherPadding = new ISO10126d2Padding();
} else if ("PKCS7".equalsIgnoreCase(name) || "PKCS5".equalsIgnoreCase(name)) {
blockCipherPadding = new PKCS7Padding();
} else if ("TBC".equalsIgnoreCase(name)) {
blockCipherPadding = new TBCPadding();
} else if ("X923".equalsIgnoreCase(name)) {
blockCipherPadding = new X923Padding();
} else if ("NULL".equalsIgnoreCase(name) || "Zero".equalsIgnoreCase(name) || "None".equalsIgnoreCase(name)) {
blockCipherPadding = new ZeroBytePadding();
} else {
throw new IllegalArgumentException("Invalid padding " + padding);
}
return blockCipherPadding;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/spec/CodecSpec.java 0000664 0000000 0000000 00000006113 14226043123 0025247 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.spec;
import org.cryptacular.codec.Base32Codec;
import org.cryptacular.codec.Base64Codec;
import org.cryptacular.codec.Codec;
import org.cryptacular.codec.HexCodec;
/**
* Describes a string-to-byte encoding provides a means to create a new instance of the coed via the {@link
* #newInstance()} method.
*
* @author Middleware Services
*/
public class CodecSpec implements Spec
* 25:48:2f:28:ec:5d:19:bb:1d:25:ae:94:93:b1:7b:b5:35:96:24:66
.
*
* @param cert Certificate to process.
*
* @return Subject key identifier in colon-delimited hex format.
*
* @throws EncodingException on cert field extraction.
*/
public static String subjectKeyId(final X509Certificate cert) throws EncodingException
{
return CodecUtil.hex(new ExtensionReader(cert).readSubjectKeyIdentifier().getKeyIdentifier(), true);
}
/**
* Gets the authority key identifier of the given certificate in delimited hexadecimal format, e.g.
* 25:48:2f:28:ec:5d:19:bb:1d:25:ae:94:93:b1:7b:b5:35:96:24:66
.
*
* @param cert Certificate to process.
*
* @return Authority key identifier in colon-delimited hex format.
*
* @throws EncodingException on cert field extraction.
*/
public static String authorityKeyId(final X509Certificate cert) throws EncodingException
{
return CodecUtil.hex(new ExtensionReader(cert).readAuthorityKeyIdentifier().getKeyIdentifier(), true);
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/util/CipherUtil.java 0000664 0000000 0000000 00000035233 14226043123 0025517 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.function.Function;
import javax.crypto.SecretKey;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.cryptacular.CiphertextHeader;
import org.cryptacular.CiphertextHeaderV2;
import org.cryptacular.CryptoException;
import org.cryptacular.EncodingException;
import org.cryptacular.StreamException;
import org.cryptacular.adapter.AEADBlockCipherAdapter;
import org.cryptacular.adapter.BlockCipherAdapter;
import org.cryptacular.adapter.BufferedBlockCipherAdapter;
import org.cryptacular.generator.Nonce;
/**
* Utility class that performs encryption and decryption operations using a block cipher.
*
* @author Middleware Services
*/
public final class CipherUtil
{
/** Mac size in bits. */
private static final int MAC_SIZE_BITS = 128;
/** Private constructor of utility class. */
private CipherUtil() {}
/**
* Encrypts data using an AEAD cipher. A {@link CiphertextHeaderV2} is prepended to the resulting ciphertext and
* used as AAD (Additional Authenticated Data) passed to the AEAD cipher.
*
* @param cipher AEAD cipher.
* @param key Encryption key.
* @param nonce Nonce generator.
* @param data Plaintext data to be encrypted.
*
* @return Concatenation of encoded {@link CiphertextHeaderV2} and encrypted data that completely fills the returned
* byte array.
*
* @throws CryptoException on encryption errors.
*/
public static byte[] encrypt(final AEADBlockCipher cipher, final SecretKey key, final Nonce nonce, final byte[] data)
throws CryptoException
{
final byte[] iv = nonce.generate();
final byte[] header = new CiphertextHeaderV2(iv, "1").encode(key);
cipher.init(true, new AEADParameters(new KeyParameter(key.getEncoded()), MAC_SIZE_BITS, iv, header));
return encrypt(new AEADBlockCipherAdapter(cipher), header, data);
}
/**
* Encrypts data using an AEAD cipher. A {@link CiphertextHeaderV2} is prepended to the resulting ciphertext and used
* as AAD (Additional Authenticated Data) passed to the AEAD cipher.
*
* @param cipher AEAD cipher.
* @param key Encryption key.
* @param nonce Nonce generator.
* @param input Input stream containing plaintext data.
* @param output Output stream that receives a {@link CiphertextHeaderV2} followed by ciphertext data produced by
* the AEAD cipher in encryption mode.
*
* @throws CryptoException on encryption errors.
* @throws StreamException on IO errors.
*/
public static void encrypt(
final AEADBlockCipher cipher,
final SecretKey key,
final Nonce nonce,
final InputStream input,
final OutputStream output)
throws CryptoException, StreamException
{
final byte[] iv = nonce.generate();
final byte[] header = new CiphertextHeaderV2(iv, "1").encode(key);
cipher.init(true, new AEADParameters(new KeyParameter(key.getEncoded()), MAC_SIZE_BITS, iv, header));
writeHeader(header, output);
process(new AEADBlockCipherAdapter(cipher), input, output);
}
/**
* Decrypts data using an AEAD cipher.
*
* @param cipher AEAD cipher.
* @param key Encryption key.
* @param data Ciphertext data containing a prepended {@link CiphertextHeaderV2}. The header is treated as AAD input
* to the cipher that is verified during decryption.
*
* @return Decrypted data that completely fills the returned byte array.
*
* @throws CryptoException on encryption errors.
* @throws EncodingException on decoding cyphertext header.
*/
public static byte[] decrypt(final AEADBlockCipher cipher, final SecretKey key, final byte[] data)
throws CryptoException, EncodingException
{
final CiphertextHeader header = decodeHeader(data, String -> key);
final byte[] nonce = header.getNonce();
final byte[] hbytes = header.encode();
cipher.init(false, new AEADParameters(new KeyParameter(key.getEncoded()), MAC_SIZE_BITS, nonce, hbytes));
return decrypt(new AEADBlockCipherAdapter(cipher), data, header.getLength());
}
/**
* Decrypts data using an AEAD cipher.
*
* @param cipher AEAD cipher.
* @param key Encryption key.
* @param input Input stream containing a {@link CiphertextHeaderV2} followed by ciphertext data. The header is
* treated as AAD input to the cipher that is verified during decryption.
* @param output Output stream that receives plaintext produced by block cipher in decryption mode.
*
* @throws CryptoException on encryption errors.
* @throws EncodingException on decoding cyphertext header.
* @throws StreamException on IO errors.
*/
public static void decrypt(
final AEADBlockCipher cipher,
final SecretKey key,
final InputStream input,
final OutputStream output)
throws CryptoException, EncodingException, StreamException
{
final CiphertextHeader header = decodeHeader(input, String -> key);
final byte[] nonce = header.getNonce();
final byte[] hbytes = header.encode();
cipher.init(false, new AEADParameters(new KeyParameter(key.getEncoded()), MAC_SIZE_BITS, nonce, hbytes));
process(new AEADBlockCipherAdapter(cipher), input, output);
}
/**
* Encrypts data using the given block cipher with PKCS5 padding. A {@link CiphertextHeaderV2} is prepended to the
* resulting ciphertext.
*
* @param cipher Block cipher.
* @param key Encryption key.
* @param nonce IV generator. Callers must take care to ensure that the length of generated IVs is equal to the
* cipher block size.
* @param data Plaintext data to be encrypted.
*
* @return Concatenation of encoded {@link CiphertextHeaderV2} and encrypted data that completely fills the returned
* byte array.
*
* @throws CryptoException on encryption errors.
*/
public static byte[] encrypt(final BlockCipher cipher, final SecretKey key, final Nonce nonce, final byte[] data)
throws CryptoException
{
final byte[] iv = nonce.generate();
final byte[] header = new CiphertextHeaderV2(iv, "1").encode(key);
final PaddedBufferedBlockCipher padded = new PaddedBufferedBlockCipher(cipher, new PKCS7Padding());
padded.init(true, new ParametersWithIV(new KeyParameter(key.getEncoded()), iv));
return encrypt(new BufferedBlockCipherAdapter(padded), header, data);
}
/**
* Encrypts data using the given block cipher with PKCS5 padding. A {@link CiphertextHeader} is prepended to the
* resulting ciphertext.
*
* @param cipher Block cipher.
* @param key Encryption key.
* @param nonce IV generator. Callers must take care to ensure that the length of generated IVs is equal to the
* cipher block size.
* @param input Input stream containing plaintext data.
* @param output Output stream that receives ciphertext produced by block cipher in encryption mode.
*
* @throws CryptoException on encryption errors.
* @throws StreamException on IO errors.
*/
public static void encrypt(
final BlockCipher cipher,
final SecretKey key,
final Nonce nonce,
final InputStream input,
final OutputStream output)
throws CryptoException, StreamException
{
final byte[] iv = nonce.generate();
final byte[] header = new CiphertextHeaderV2(iv, "1").encode(key);
final PaddedBufferedBlockCipher padded = new PaddedBufferedBlockCipher(cipher, new PKCS7Padding());
padded.init(true, new ParametersWithIV(new KeyParameter(key.getEncoded()), iv));
writeHeader(header, output);
process(new BufferedBlockCipherAdapter(padded), input, output);
}
/**
* Decrypts data using the given block cipher with PKCS5 padding.
*
* @param cipher Block cipher.
* @param key Encryption key.
* @param data Ciphertext data containing a prepended {@link CiphertextHeader}.
*
* @return Decrypted data that completely fills the returned byte array.
*
* @throws CryptoException on encryption errors.
* @throws EncodingException on decoding cyphertext header.
*/
public static byte[] decrypt(final BlockCipher cipher, final SecretKey key, final byte[] data)
throws CryptoException, EncodingException
{
final CiphertextHeader header = decodeHeader(data, String -> key);
final PaddedBufferedBlockCipher padded = new PaddedBufferedBlockCipher(cipher, new PKCS7Padding());
padded.init(false, new ParametersWithIV(new KeyParameter(key.getEncoded()), header.getNonce()));
return decrypt(new BufferedBlockCipherAdapter(padded), data, header.getLength());
}
/**
* Decrypts data using the given block cipher with PKCS5 padding.
*
* @param cipher Block cipher.
* @param key Encryption key.
* @param input Input stream containing a {@link CiphertextHeader} followed by ciphertext data.
* @param output Output stream that receives plaintext produced by block cipher in decryption mode.
*
* @throws CryptoException on encryption errors.
* @throws EncodingException on decoding cyphertext header.
* @throws StreamException on IO errors.
*/
public static void decrypt(
final BlockCipher cipher,
final SecretKey key,
final InputStream input,
final OutputStream output)
throws CryptoException, EncodingException, StreamException
{
final CiphertextHeader header = decodeHeader(input, String -> key);
final PaddedBufferedBlockCipher padded = new PaddedBufferedBlockCipher(cipher, new PKCS7Padding());
padded.init(false, new ParametersWithIV(new KeyParameter(key.getEncoded()), header.getNonce()));
process(new BufferedBlockCipherAdapter(padded), input, output);
}
/**
* Decodes the ciphertext header at the start of the given byte array.
* Supports both original (deprecated) and v2 formats.
*
* @param data Ciphertext data with prepended header.
* @param keyLookup Decryption key lookup function.
*
* @return Ciphertext header instance.
*/
public static CiphertextHeader decodeHeader(final byte[] data, final Function// data is a byte array containing raw data to digest final byte[] salt = new RBGNonce(16).generate(); final byte[] hash = HashUtil.hash(new SHA1Digest(), data, salt); ** * @param digest Hash algorithm. * @param data Data to hash. Supported types are
byte[]
, {@link CharSequence} ,{@link InputStream}, and
* {@link Resource}. Character data is processed in the UTF-8
character set; if another
* character set is desired, the caller should convert to byte[]
and provide the resulting
* bytes.
*
* @return Byte array of length {@link Digest#getDigestSize()} containing hash output.
*
* @throws CryptoException on hash computation errors.
* @throws StreamException on stream IO errors.
*/
public static byte[] hash(final Digest digest, final Object... data) throws CryptoException, StreamException
{
for (Object o : data) {
if (o instanceof byte[]) {
final byte[] bytes = (byte[]) o;
digest.update(bytes, 0, bytes.length);
} else if (o instanceof String) {
final byte[] bytes = ByteUtil.toBytes((String) o);
digest.update(bytes, 0, bytes.length);
} else if (o instanceof InputStream) {
hashStream(digest, (InputStream) o);
} else if (o instanceof Resource) {
final InputStream in;
try {
in = ((Resource) o).getInputStream();
} catch (IOException e) {
throw new StreamException(e);
}
hashStream(digest, in);
} else {
throw new IllegalArgumentException("Invalid input data type " + o);
}
}
final byte[] output = new byte[digest.getDigestSize()];
try {
digest.doFinal(output, 0);
} catch (RuntimeException e) {
throw new CryptoException("Hash computation error", e);
}
return output;
}
/**
* Computes the iterated hash of the given data using the given algorithm. The following example demonstrates a
* typical usage pattern, a salted hash with 10 rounds:
*
* // data is a byte array containing raw data to digest final byte[] salt = new RBGNonce(16).generate(); final byte[] hash = HashUtil.hash(new SHA1Digest(), 10, data, salt); ** * @param digest Hash algorithm. * @param iterations Number of hash rounds. Must be positive value. * @param data Data to hash. Supported types are
byte[]
, {@link CharSequence} ,{@link InputStream}, and
* {@link Resource}. Character data is processed in the UTF-8
character set; if another
* character set is desired, the caller should convert to byte[]
and provide the resulting
* bytes.
*
* @return Byte array of length {@link Digest#getDigestSize()} containing hash output.
*
* @throws CryptoException on hash computation errors.
* @throws StreamException on stream IO errors.
*/
public static byte[] hash(final Digest digest, final int iterations, final Object... data)
throws CryptoException, StreamException
{
if (iterations < 1) {
throw new IllegalArgumentException("Iterations must be positive");
}
final byte[] output = hash(digest, data);
try {
for (int i = 1; i < iterations; i++) {
digest.update(output, 0, output.length);
digest.doFinal(output, 0);
}
} catch (RuntimeException e) {
throw new CryptoException("Hash computation error", e);
}
return output;
}
/**
* Determines whether the hash of the given input equals a known value.
*
* @param digest Hash algorithm.
* @param hash Hash to compare with. If the length of the array is greater than the length of the digest output,
* anything beyond the digest length is considered salt data that is hashed after the
* input data.
* @param iterations Number of hash rounds.
* @param data Data to hash.
*
* @return True if the hash of the data under the given digest is equal to the hash, false otherwise.
*
* @throws CryptoException on hash computation errors.
* @throws StreamException on stream IO errors.
*/
public static boolean compareHash(final Digest digest, final byte[] hash, final int iterations, final Object... data)
throws CryptoException, StreamException
{
if (hash.length > digest.getDigestSize()) {
final byte[] hashPart = Arrays.copyOfRange(hash, 0, digest.getDigestSize());
final byte[] saltPart = Arrays.copyOfRange(hash, digest.getDigestSize(), hash.length);
final Object[] dataWithSalt = Arrays.copyOf(data, data.length + 1);
dataWithSalt[data.length] = saltPart;
return Arrays.equals(hash(digest, iterations, dataWithSalt), hashPart);
}
return Arrays.equals(hash(digest, iterations, data), hash);
}
/**
* Determines whether the salted hash of the given input equals a known hash value.
*
* @param digest Hash algorithm.
* @param hash Salted hash data.
* @param iterations Number of hash rounds.
* @param saltAfterData True to apply salt after data, false to apply salt before data.
* @param data Data to hash, which should NOT include the salt value.
*
* @return True if the hash of the data under the given digest is equal to the hash, false otherwise.
*
* @throws CryptoException on hash computation errors.
* @throws StreamException on stream IO errors.
*/
public static boolean compareHash(
final Digest digest,
final SaltedHash hash,
final int iterations,
final boolean saltAfterData,
final Object... data)
throws CryptoException, StreamException
{
final Object[] dataWithSalt;
if (saltAfterData) {
dataWithSalt = Arrays.copyOf(data, data.length + 1);
dataWithSalt[data.length] = hash.getSalt();
} else {
dataWithSalt = new Object[data.length + 1];
dataWithSalt[0] = hash.getSalt();
System.arraycopy(data, 0, dataWithSalt, 1, data.length);
}
return Arrays.equals(hash(digest, iterations, dataWithSalt), hash.getHash());
}
/**
* Produces the SHA-1 hash of the given data.
*
* @param data Data to hash. See {@link #hash(Digest, Object...)} for supported inputs.
*
* @return 20-byte array containing hash output.
*
* @see #hash(Digest, Object...)
*/
public static byte[] sha1(final Object... data)
{
return hash(new SHA1Digest(), data);
}
/**
* Produces the SHA-256 hash of the given data.
*
* @param data Data to hash. See {@link #hash(Digest, Object...)} for supported inputs.
*
* @return 32-byte array containing hash output.
*
* @see #hash(Digest, Object...)
*/
public static byte[] sha256(final Object... data)
{
return hash(new SHA256Digest(), data);
}
/**
* Produces the SHA-512 hash of the given data.
*
* @param data Data to hash. See {@link #hash(Digest, Object...)} for supported inputs.
*
* @return 64-byte array containing hash output.
*
* @see #hash(Digest, Object...)
*/
public static byte[] sha512(final Object... data)
{
return hash(new SHA512Digest(), data);
}
/**
* Produces the SHA-3 hash of the given data.
*
* @param bitLength One of the supported SHA-3 output bit lengths: 224, 256, 384, or 512.
* @param data Data to hash. See {@link #hash(Digest, Object...)} for supported inputs.
*
* @return Byte array of size bitLength
containing hash output.
*
* @see #hash(Digest, Object...)
*/
public static byte[] sha3(final int bitLength, final Object... data)
{
return hash(new SHA3Digest(bitLength), data);
}
/**
* Digests the data in the given stream. Note this method does not finalize the digest process by calling {@link
* Digest#doFinal(byte[], int)}.
*
* @param digest Digest algorithm.
* @param in Input stream containing data to hash.
*/
private static void hashStream(final Digest digest, final InputStream in)
{
final byte[] buffer = new byte[StreamUtil.CHUNK_SIZE];
int length;
try {
while ((length = in.read(buffer)) > 0) {
digest.update(buffer, 0, length);
}
} catch (IOException e) {
throw new StreamException(e);
}
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/util/KeyPairUtil.java 0000664 0000000 0000000 00000040021 14226043123 0025640 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.signers.DSASigner;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.signers.RSADigestSigner;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.cryptacular.EncodingException;
import org.cryptacular.StreamException;
import org.cryptacular.adapter.Converter;
import org.cryptacular.asn.OpenSSLPrivateKeyDecoder;
import org.cryptacular.asn.PKCS8PrivateKeyDecoder;
import org.cryptacular.asn.PublicKeyDecoder;
/**
* Utility methods for public/private key pairs used for asymmetric encryption.
*
* @author Middleware Services
*/
public final class KeyPairUtil
{
/** Data used to verify key pairs. */
private static final byte[] SIGN_BYTES = ByteUtil.toBytes("Mr. Watson--come here--I want to see you.");
/** Private constructor of utility class. */
private KeyPairUtil() {}
/**
* Gets the length in bits of a public key where key size is dependent on the particulars of the algorithm.
*
* Keys from the following asymmetric algorithms are supported:
* *This nonce generation strategy is suitable for GCM ciphers.
* * @param length Number of bytes in nonce; MUST be 12 or more. * * @return Nonce bytes. */ public static byte[] nist80038d(final int length) { return new RBGNonce(length).generate(); } /** * Generates a random IV according to NIST SP-800-63a, appendix C, method 1 * (encrypted nonce), suitable for use with any block cipher mode described in that standard. This method uses an * instance of {@link EncryptedNonce} for the implementation. * * @param cipher Block cipher. * @param key Encryption key intended for use with IV. * * @return Cipher block size number of random bytes. * * @see EncryptedNonce */ public static byte[] nist80063a(final BlockCipher cipher, final SecretKey key) { BlockCipher raw = cipher; // Get the underlying cipher if there is one final Method method = ReflectUtil.getMethod(cipher.getClass(), "getUnderlyingCipher"); if (method != null) { raw = (BlockCipher) ReflectUtil.invoke(cipher, method); } return new EncryptedNonce(raw, key).generate(); } /** * Generates a random IV according to NIST SP-800-63a, appendix C, method 2 * (pseudorandom), suitable for use with any block cipher mode described in that standard. * * @param prng NIST SP800-63a approved pseudorandom number generator. * @param blockSize Cipher block size in bytes. * * @return Cipher block size number of random bytes. */ public static byte[] nist80063a(final SP800SecureRandom prng, final int blockSize) { prng.setSeed(randomNonce(blockSize)); final byte[] iv = new byte[blockSize]; prng.nextBytes(iv); return iv; } /** * Generates a random IV according to NIST SP-800-63a, appendix C, method 2 * (pseudorandom), suitable for use with any block cipher mode described in that standard. Uses an instance of {@link * RBGNonce} internally with length equal to block size of given cipher. * * @param cipher Block cipher. * * @return Cipher block size number of random bytes. * * @see RBGNonce */ public static byte[] nist80063a(final BlockCipher cipher) { return new RBGNonce(cipher.getBlockSize()).generate(); } /** * Creates a new DRBG instance based on a SHA-256 digest. * * @param length Length in bits of values to be produced by DRBG instance. * * @return New DRGB instance. */ public static SP80090DRBG newRBG(final int length) { return newRBG(new SHA256Digest(), length); } /** * Creates a new hash-based DRBG instance that uses the given digest as the pseudorandom source. * * @param digest Digest algorithm. * @param length Length in bits of values to be produced by DRBG instance. * * @return New DRGB instance. */ public static SP80090DRBG newRBG(final Digest digest, final int length) { return newRBG(digest, length, randomEntropySource(length)); } /** * Creates a new hash-based DRBG instance that uses the given digest as the pseudorandom source. * * @param digest Digest algorithm. * @param length Length in bits of values to be produced by DRBG instance. * @param es Entropy source. * * @return New DRGB instance. */ public static SP80090DRBG newRBG(final Digest digest, final int length, final EntropySource es) { return new HashSP800DRBG( digest, length, es, Thread.currentThread().getName().getBytes(), NonceUtil.timestampNonce(8)); } } cryptacular-1.2.5/src/main/java/org/cryptacular/util/PemUtil.java 0000664 0000000 0000000 00000010526 14226043123 0025024 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.util; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.regex.Pattern; import org.cryptacular.codec.Base64Decoder; /** * Utility class with helper methods for common PEM encoding operations. * * @author Middleware Services */ public final class PemUtil { /** Line length. */ public static final int LINE_LENGTH = 64; /** PEM encoding header start string. */ public static final String HEADER_BEGIN = "-----BEGIN"; /** PEM encoding footer start string. */ public static final String FOOTER_END = "-----END"; /** Procedure type tag for PEM-encoded private key in OpenSSL format. */ public static final String PROC_TYPE = "Proc-Type:"; /** Decryption infor tag for PEM-encoded private key in OpenSSL format. */ public static final String DEK_INFO = "DEK-Info:"; /** Pattern used to split multiple PEM-encoded objects in a single file. */ private static final Pattern PEM_SPLITTER = Pattern.compile("-----(?:BEGIN|END) [A-Z ]+-----"); /** Pattern used to a file by line terminator. */ private static final Pattern LINE_SPLITTER = Pattern.compile("[\r\n]+"); /** Private constructor of utility class. */ private PemUtil() {} /** * Determines whether the data in the given byte array is base64-encoded data of PEM encoding. The determination is * made using as little data from the given array as necessary to make a reasonable determination about encoding. * * @param data Data to test for PEM encoding * * @return True if data appears to be PEM encoded, false otherwise. */ public static boolean isPem(final byte[] data) { final String start = new String(data, 0, 10, ByteUtil.ASCII_CHARSET).trim(); if (!start.startsWith(HEADER_BEGIN) && !start.startsWith(PROC_TYPE)) { // Check all bytes in first line to make sure they are in the range // of base64 character set encoding for (int i = 0; i < LINE_LENGTH; i++) { if (!isBase64Char(data[i])) { // Last two bytes may be padding character '=' (61) if (i > LINE_LENGTH - 3) { if (data[i] != 61) { return false; } } else { return false; } } } } return true; } /** * Determines whether the given byte represents an ASCII character in the character set for base64 encoding. * * @param b Byte to test. * * @return True if the byte represents an ASCII character in the set of valid characters for base64 encoding, false * otherwise. The padding character '=' is not considered valid since it may only appear at the end of a * base64 encoded value. */ public static boolean isBase64Char(final byte b) { return !(b < 47 || b > 122 || b > 57 && b < 65 || b > 90 && b < 97) || b == 43; } /** * Decodes a PEM-encoded cryptographic object into the raw bytes of its ASN.1 encoding. Header/footer data and * metadata info, e.g. Proc-Type, are ignored. * * @param pem Bytes of PEM-encoded data to decode. * * @return ASN.1 encoded bytes. */ public static byte[] decode(final byte[] pem) { return decode(new String(pem, ByteUtil.ASCII_CHARSET)); } /** * Decodes one or more PEM-encoded cryptographic objects into the raw bytes of their ASN.1 encoding. All header and * metadata, e.g. Proc-Type, are ignored. If multiple cryptographic objects are represented, the decoded bytes of * each object are concatenated together and returned. * * @param pem PEM-encoded data to decode. * * @return ASN.1 encoded bytes. */ public static byte[] decode(final String pem) { final Base64Decoder decoder = new Base64Decoder(); final CharBuffer buffer = CharBuffer.allocate(pem.length()); final ByteBuffer output = ByteBuffer.allocate(pem.length() * 3 / 4); // There may be multiple PEM-encoded objects in the input for (String object : PEM_SPLITTER.split(pem)) { buffer.clear(); for (String line : LINE_SPLITTER.split(object)) { if (line.startsWith(DEK_INFO) || line.startsWith(PROC_TYPE)) { continue; } buffer.append(line); } buffer.flip(); decoder.decode(buffer, output); decoder.finalize(output); } output.flip(); return ByteUtil.toArray(output); } } cryptacular-1.2.5/src/main/java/org/cryptacular/util/ReflectUtil.java 0000664 0000000 0000000 00000003357 14226043123 0025673 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.util; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /** * Reflection utilities. * * @author Middleware Services */ public final class ReflectUtil { /** Method cache. */ private static final MapIssuerAlternativeName
extension field of the certificate.
*
* @return Collection of issuer alternative names or null if the certificate does not define this extension field.
* Note that an empty collection of names is different from a null return value; in the former case the field
* is defined but empty, whereas in the latter the field is not defined on the certificate.
*
* @throws EncodingException On certificate field parse errors.
*/
public GeneralNames readIssuerAlternativeName() throws EncodingException
{
try {
return GeneralNames.getInstance(read(ExtensionType.IssuerAlternativeName));
} catch (RuntimeException e) {
throw new EncodingException("GeneralNames parse error", e);
}
}
/**
* Reads the value of the BasicConstraints
extension field of the certificate.
*
* @return Basic constraints defined on certificate or null if the certificate does not define the field.
*
* @throws EncodingException On certificate field parse errors.
*/
public BasicConstraints readBasicConstraints() throws EncodingException
{
try {
return BasicConstraints.getInstance(read(ExtensionType.BasicConstraints));
} catch (RuntimeException e) {
throw new EncodingException("BasicConstraints parse error", e);
}
}
/**
* Reads the value of the CertificatePolicies
extension field of the certificate.
*
* @return List of certificate policies defined on certificate or null if the certificate does not define the field.
*
* @throws EncodingException On certificate field parse errors.
*/
public ListSubjectKeyIdentifier
extension field of the certificate.
*
* @return Subject key identifier.
*
* @throws EncodingException On certificate field parse errors.
*/
public SubjectKeyIdentifier readSubjectKeyIdentifier() throws EncodingException
{
try {
return SubjectKeyIdentifier.getInstance(read(ExtensionType.SubjectKeyIdentifier));
} catch (RuntimeException e) {
throw new EncodingException("SubjectKeyIdentifier parse error", e);
}
}
/**
* Reads the value of the AuthorityKeyIdentifier
extension field of the certificate.
*
* @return Authority key identifier.
*
* @throws EncodingException On certificate field parse errors.
*/
public AuthorityKeyIdentifier readAuthorityKeyIdentifier() throws EncodingException
{
try {
return AuthorityKeyIdentifier.getInstance(read(ExtensionType.AuthorityKeyIdentifier));
} catch (RuntimeException e) {
throw new EncodingException("AuthorityKeyIdentifier parse error", e);
}
}
/**
* Reads the value of the KeyUsage
extension field of the certificate.
*
* @return Key usage data or null if extension field is not defined.
*
* @throws EncodingException On certificate field parse errors.
*/
public KeyUsage readKeyUsage() throws EncodingException
{
try {
return KeyUsage.getInstance(read(ExtensionType.KeyUsage));
} catch (RuntimeException e) {
throw new EncodingException("KeyUsage parse error", e);
}
}
/**
* Reads the value of the ExtendedKeyUsage
extension field of the certificate.
*
* @return List of supported extended key usages or null if extension is not defined.
*
* @throws EncodingException On certificate field parse errors.
*/
public ListCRLDistributionPoints
extension field of the certificate.
*
* @return List of CRL distribution points or null if extension is not defined.
*
* @throws EncodingException On certificate field parse errors.
*/
public ListAuthorityInformationAccess
extension field of the certificate.
*
* @return List of access descriptions or null if extension is not defined.
*
* @throws EncodingException On certificate field parse errors.
*/
public ListGeneralName
* type defined in section 4.2.1.7 of RFC 2459.
*
* @author Middleware Services
*/
public enum GeneralNameType {
/** otherName choice element. */
OtherName,
/** rfc822Name choice element. */
RFC822Name,
/** dNSName choice element. */
DNSName,
/** x400Address choice element. */
X400Address,
/** directoryName choice element. */
DirectoryName,
/** ediPartyName choice element. */
EdiPartyName,
/** uniformResourceIdentifier choice element. */
UniformResourceIdentifier,
/** iPAddress choice element. */
IPAddress,
/** registeredID choice element. */
RegisteredID;
/** Minimum tag number for items in CHOICE definition. */
public static final int MIN_TAG_NUMBER = 0;
/** Maximum tag number for items in CHOICE definition. */
public static final int MAX_TAG_NUMBER = 8;
/**
* Gets a name type from the value of the tag in the CHOICE element definition.
*
* @param tagNo Ordinal position of type in CHOICE definition in RFC 2459.
*
* @return Type corresponding to given tag number.
*
* @throws IllegalArgumentException If there is not general name type corresponding to the given tag number.
*/
public static GeneralNameType fromTagNumber(final int tagNo)
{
if (tagNo < MIN_TAG_NUMBER || tagNo > MAX_TAG_NUMBER) {
throw new IllegalArgumentException("Invalid tag number " + tagNo);
}
for (GeneralNameType type : values()) {
if (type.ordinal() == tagNo) {
return type;
}
}
throw new IllegalArgumentException("Invalid tag number " + tagNo);
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/x509/KeyUsageBits.java 0000664 0000000 0000000 00000004643 14226043123 0025537 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.x509;
import java.util.BitSet;
import org.bouncycastle.asn1.x509.KeyUsage;
/**
* Representation of the bit meanings in the KeyUsage
BIT STRING type defined in section 4.2.1.3 of RFC
* 2459.
*
* @author Middleware Services
* @version $Revision: 2745 $
*/
public enum KeyUsageBits {
/** digitalSignature bit. */
DigitalSignature(7),
/** nonRepudiation bit. */
NonRepudiation(6),
/** keyEncipherment bit. */
KeyEncipherment(5),
/** dataEncipherment bit. */
DataEncipherment(4),
/** keyAgreement bit. */
KeyAgreement(3),
/** keyCertSign bit. */
KeyCertSign(2),
/** cRLSign bit. */
CRLSign(1),
/** encipherOnly bit. */
EncipherOnly(0),
/** decipherOnly bit. */
DecipherOnly(15);
/** Bit mask offset. */
private final int offset;
/**
* Creates a bit flag with the given bit mask offset.
*
* @param offset Bit mask offset.
*/
KeyUsageBits(final int offset)
{
this.offset = offset;
}
/** @return Bit mask value. */
public int getMask()
{
return 1 << offset;
}
/**
* Determines whether this key usage bit is set in the given key usage value.
*
* @param keyUsage BC key usage object.
*
* @return True if bit is set, false otherwise.
*/
public boolean isSet(final KeyUsage keyUsage)
{
return isSet(keyUsage.getBytes());
}
/**
* Determines whether this key usage bit is set in the given key usage bit string.
*
* @param bitString Key usage bit string as a byte array.
*
* @return True if bit is set, false otherwise.
*/
public boolean isSet(final byte[] bitString)
{
return BitSet.valueOf(bitString).get(offset);
}
/**
* Determines whether this key usage bit is set in the given key usage bit string.
*
* @param bitString Key usage bit string as a big endian integer.
*
* @return True if bit is set, false otherwise.
*/
public boolean isSet(final int bitString)
{
return (bitString & getMask()) >> offset == 1;
}
/**
* Computes the key usage value from one or more key usage bits.
*
* @param bits One ore more key usage bits.
*
* @return Key usage bit string as an integer.
*/
public static int usage(final KeyUsageBits... bits)
{
int usage = 0;
for (KeyUsageBits bit : bits) {
usage |= bit.getMask();
}
return usage;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/x509/dn/ 0000775 0000000 0000000 00000000000 14226043123 0022727 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/main/java/org/cryptacular/x509/dn/Attribute.java 0000664 0000000 0000000 00000002070 14226043123 0025534 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.x509.dn;
/**
* Simple implementation of the X.501 AttributeTypeAndValue that makes up the RelativeDistinguishedName type described
* in section 4.1.2.4 of RFC 2459.
*
* @author Middleware Services
*/
public class Attribute
{
/** Attribute type. */
private final AttributeType type;
/** Attribute value. */
private final String value;
/**
* Creates a new instance of the given type and value.
*
* @param type Attribute type.
* @param value Attribute value.
*/
public Attribute(final AttributeType type, final String value)
{
if (type == null) {
throw new IllegalArgumentException("Type cannot be null.");
}
this.type = type;
if (value == null) {
throw new IllegalArgumentException("Value cannot be null.");
}
this.value = value;
}
/** @return Attribute type. */
public AttributeType getType()
{
return type;
}
/** @return Attribute value. */
public String getValue()
{
return value;
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/x509/dn/AttributeType.java 0000664 0000000 0000000 00000000630 14226043123 0026376 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.x509.dn;
/**
* Describes values of AttributeType that may appear in a RelativeDistinguishedName (RDN) as defined in section 2 of RFC
* 2253.
*
* @author Middleware Services
*/
public interface AttributeType
{
/** @return Attribute OID. */
String getOid();
/** @return Attribute name. */
String getName();
}
cryptacular-1.2.5/src/main/java/org/cryptacular/x509/dn/Attributes.java 0000664 0000000 0000000 00000005450 14226043123 0025724 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.x509.dn;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
/**
* Ordered list of {@link Attribute}s.
*
* @author Middleware Services
*/
public class Attributes implements IterableCN=foo, OU=Bar, dc=example,
* dc=com
.
*/
@Override
public String toString()
{
final StringBuilder builder = new StringBuilder();
int i = 0;
for (RDN rdn : this) {
for (Attribute attr : rdn.getAttributes()) {
if (i++ > 0) {
builder.append(", ");
}
builder.append(attr.getType()).append('=').append(attr.getValue());
}
}
return builder.toString();
}
}
cryptacular-1.2.5/src/main/java/org/cryptacular/x509/dn/StandardAttributeType.java 0000664 0000000 0000000 00000011334 14226043123 0030062 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.x509.dn;
/**
* Describes the registered values of AttributeType that may appear in a RelativeDistinguishedName (RDN) as defined in
* section 2 of RFC 2253.
*
* Enumeration values include attributes likely to appear in an X.509 RDN, which were obtained from the following * sources:
* *@DataProvider
errors.
*
* @author Middleware Services
*/
public class FailListener extends TestListenerAdapter
{
@Override
public void onTestSkipped(final ITestResult tr)
{
if (tr.getThrowable() != null) {
tr.setStatus(ITestResult.FAILURE);
}
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/adapter/ 0000775 0000000 0000000 00000000000 14226043123 0023274 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/test/java/org/cryptacular/adapter/WrappedKeyTest.java 0000664 0000000 0000000 00000005706 14226043123 0027062 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.adapter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.cryptacular.FailListener;
import org.cryptacular.util.KeyPairUtil;
import org.cryptacular.util.StreamUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.AssertJUnit.assertTrue;
/**
* Test for {@link AbstractWrappedKey} classes.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class WrappedKeyTest
{
private static final String KEY_PATH = "src/test/resources/keys/";
@DataProvider(name = "keypairs")
public Object[][] getKeyPairs()
{
return
new Object[][] {
{"DSA", KEY_PATH + "dsa-pub.der", KEY_PATH + "dsa-pkcs8-nopass.der", },
{"RSA", KEY_PATH + "rsa-pub.der", KEY_PATH + "rsa-pkcs8-nopass.der", },
// TODO: enable once BC gets support for writing EC named curves
// As of bcprov 1.50 only raw EC params can be written
// SunJCE only understands named curves
// {
// "EC",
// KEY_PATH + "ec-prime256v1-named-pub.der",
// KEY_PATH + "ec-pkcs8-prime256v1-named-nopass.der",
// },
};
}
@Test(dataProvider = "keypairs")
public void testKeyEquivalence(final String algorithm, final String pubKeyPath, final String privKeyPath)
throws Exception
{
final KeyPair wrappedPair = new KeyPair(
KeyPairUtil.readPublicKey(pubKeyPath),
KeyPairUtil.readPrivateKey(privKeyPath));
final String bcPubKeyPath = String.format("target/%s-%s.key", algorithm, "pub");
final String bcPrivKeyPath = String.format("target/%s-%s.key", algorithm, "priv");
writeFile(bcPubKeyPath, wrappedPair.getPublic().getEncoded());
writeFile(bcPrivKeyPath, wrappedPair.getPrivate().getEncoded());
final KeyPair jcePair = readJCEKeyPair(algorithm, bcPubKeyPath, bcPrivKeyPath);
assertTrue(KeyPairUtil.isKeyPair(wrappedPair.getPublic(), jcePair.getPrivate()));
assertTrue(KeyPairUtil.isKeyPair(jcePair.getPublic(), wrappedPair.getPrivate()));
}
private static void writeFile(final String path, final byte[] data)
throws IOException
{
try (FileOutputStream out = new FileOutputStream(path)) {
out.write(data);
}
}
private static KeyPair readJCEKeyPair(final String algorithm, final String pubKeyPath, final String privKeyPath)
throws Exception
{
final PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(StreamUtil.readAll(privKeyPath));
final X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(StreamUtil.readAll(pubKeyPath));
final KeyFactory factory = KeyFactory.getInstance(algorithm);
return new KeyPair(factory.generatePublic(pubSpec), factory.generatePrivate(privSpec));
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/ 0000775 0000000 0000000 00000000000 14226043123 0022561 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/test/java/org/cryptacular/bean/AEADBlockCipherBeanTest.java 0000664 0000000 0000000 00000012235 14226043123 0027655 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.security.KeyStore;
import org.cryptacular.FailListener;
import org.cryptacular.generator.sp80038d.CounterNonce;
import org.cryptacular.io.FileResource;
import org.cryptacular.spec.AEADBlockCipherSpec;
import org.cryptacular.util.ByteUtil;
import org.cryptacular.util.CodecUtil;
import org.cryptacular.util.StreamUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link AEADBlockCipherBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class AEADBlockCipherBeanTest
{
@DataProvider(name = "test-arrays")
public Object[][] getTestArrays()
{
return
new Object[][] {
new Object[] {
// Plaintext is NOT multiple of block size
"Able was I ere I saw elba.",
"AES/GCM",
},
// Plaintext is multiple of block size
new Object[] {
"Four score and seven years ago, our forefathers ",
"Twofish/CCM",
},
// OCB
new Object[] {
"Have you passed through this night?",
"Twofish/OCB",
},
// EAX
new Object[] {
"I went to the woods because I wished to live deliberately, to front only the essential facts of life",
"AES/EAX",
},
};
}
@DataProvider(name = "test-streams")
public Object[][] getTestStreams()
{
return
new Object[][] {
new Object[] {
"src/test/resources/plaintexts/lorem-5000.txt",
"Twofish/GCM",
},
new Object[] {
"src/test/resources/plaintexts/lorem-1200.txt",
"AES/OCB",
},
new Object[] {
"src/test/resources/plaintexts/lorem-1200.txt",
"AES/EAX",
},
};
}
@Test(dataProvider = "test-arrays")
public void testEncryptDecryptArray(final String input, final String cipherSpecString)
throws Exception
{
final AEADBlockCipherBean cipherBean = newCipherBean(AEADBlockCipherSpec.parse(cipherSpecString));
final byte[] ciphertext = cipherBean.encrypt(ByteUtil.toBytes(input));
assertEquals(ByteUtil.toString(cipherBean.decrypt(ciphertext)), input);
}
@Test(dataProvider = "test-streams")
public void testEncryptDecryptStream(final String path, final String cipherSpecString)
throws Exception
{
final AEADBlockCipherBean cipherBean = newCipherBean(AEADBlockCipherSpec.parse(cipherSpecString));
final ByteArrayOutputStream tempOut = new ByteArrayOutputStream(8192);
cipherBean.encrypt(StreamUtil.makeStream(new File(path)), tempOut);
final ByteArrayInputStream tempIn = new ByteArrayInputStream(tempOut.toByteArray());
final ByteArrayOutputStream finalOut = new ByteArrayOutputStream(8192);
cipherBean.decrypt(tempIn, finalOut);
assertEquals(ByteUtil.toString(finalOut.toByteArray()), ByteUtil.toString(StreamUtil.readAll(path)));
}
@Test
public void testDecryptArrayBackwardCompatibleHeader()
{
final AEADBlockCipherBean cipherBean = newCipherBean(new AEADBlockCipherSpec("Twofish", "OCB"));
final String expected = "Have you passed through this night?";
final String v1CiphertextHex =
"0000001f0000000c76746d770002ba17043672d900000007767463727970745a38dee735266e3f5f7aafec8d1c9ed8a0830a2ff9" +
"c3a46c25f89e69b6eb39dbb82fd13da50e32b2544a73f1a4476677b377e6";
final byte[] plaintext = cipherBean.decrypt(CodecUtil.hex(v1CiphertextHex));
assertEquals(expected, ByteUtil.toString(plaintext));
}
@Test
public void testDecryptStreamBackwardCompatibleHeader()
{
final AEADBlockCipherBean cipherBean = newCipherBean(new AEADBlockCipherSpec("Twofish", "OCB"));
final String expected = "Have you passed through this night?";
final String v1CiphertextHex =
"0000001f0000000c76746d770002ba17043672d900000007767463727970745a38dee735266e3f5f7aafec8d1c9ed8a0830a2ff9" +
"c3a46c25f89e69b6eb39dbb82fd13da50e32b2544a73f1a4476677b377e6";
final ByteArrayInputStream in = new ByteArrayInputStream(CodecUtil.hex(v1CiphertextHex));
final ByteArrayOutputStream out = new ByteArrayOutputStream();
cipherBean.decrypt(in, out);
assertEquals(expected, ByteUtil.toString(out.toByteArray()));
}
private static KeyStore getTestKeyStore()
{
final KeyStoreFactoryBean bean = new KeyStoreFactoryBean();
bean.setPassword("vtcrypt");
bean.setResource(new FileResource(new File("src/test/resources/keystores/cipher-bean.jceks")));
bean.setType("JCEKS");
return bean.newInstance();
}
private static AEADBlockCipherBean newCipherBean(final AEADBlockCipherSpec cipherSpec)
{
final AEADBlockCipherBean cipherBean = new AEADBlockCipherBean();
cipherBean.setNonce(new CounterNonce("vtmw", System.nanoTime()));
cipherBean.setKeyAlias("vtcrypt");
cipherBean.setKeyPassword("vtcrypt");
cipherBean.setKeyStore(getTestKeyStore());
cipherBean.setBlockCipherSpec(cipherSpec);
return cipherBean;
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/BCryptHashBeanTest.java 0000664 0000000 0000000 00000002600 14226043123 0027057 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
* Unit test for {@link BCryptHashBean} class.
*
* @author Middleware Services
*/
public class BCryptHashBeanTest
{
@DataProvider(name = "hashes")
public Object[][] getHashData()
{
return
new Object[][] {
{"password", "$2a$5$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe"},
{"x", "$2a$12$w6IdiZTAckGirKaH8LU8VOxEvP97cFLEW5ePVJzhZilSa5c.V/uMK"},
{"abcdefghijklmnopqrstuvwxyz", "$2a$6$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC"},
{"abcdefghijklmnopqrstuvwxyz", "$2a$8$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz."},
};
}
@Test(dataProvider = "hashes")
public void testHash(final String password, final String expected)
{
final BCryptHashBean.BCryptParameters params = new BCryptHashBean.BCryptParameters(expected);
final String hash = new BCryptHashBean(params.getCost()).hash(params.getSalt(), password);
assertEquals(params.encode(hash), expected);
}
@Test(dataProvider = "hashes")
public void testCompare(final String password, final String expected)
{
assertTrue(new BCryptHashBean(10).compare(expected, password));
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/BufferedBlockCipherBeanTest.java 0000664 0000000 0000000 00000011134 14226043123 0030702 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.math.BigInteger;
import java.security.KeyStore;
import org.cryptacular.FailListener;
import org.cryptacular.generator.Nonce;
import org.cryptacular.generator.sp80038a.BigIntegerCounterNonce;
import org.cryptacular.generator.sp80038a.LongCounterNonce;
import org.cryptacular.generator.sp80038a.RBGNonce;
import org.cryptacular.io.FileResource;
import org.cryptacular.spec.BufferedBlockCipherSpec;
import org.cryptacular.util.ByteUtil;
import org.cryptacular.util.StreamUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link BufferedBlockCipherBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class BufferedBlockCipherBeanTest
{
@DataProvider(name = "test-arrays")
public Object[][] getTestArrays()
{
return
new Object[][] {
new Object[] {
// Plaintext is NOT multiple of block size
"Able was I ere I saw elba.",
"AES/CBC/PKCS5",
new RBGNonce(16),
},
// Plaintext is multiple of block size
new Object[] {
"Four score and seven years ago, our forefathers ",
"Blowfish/CBC/None",
new RBGNonce(8),
},
// OFB
new Object[] {
"Have you passed through this night?",
"Blowfish/OFB/PKCS5Padding",
new LongCounterNonce(),
},
// CFB
new Object[] {
"I went to the woods because I wished to live deliberately, to front only the essential facts of life",
"AES/CFB/PKCS5Padding",
new RBGNonce(16),
},
};
}
@DataProvider(name = "test-streams")
public Object[][] getTestStreams()
{
return
new Object[][] {
new Object[] {
"src/test/resources/plaintexts/lorem-5000.txt",
"AES/CBC/PKCS7",
new RBGNonce(16),
},
new Object[] {
"src/test/resources/plaintexts/lorem-1200.txt",
"Twofish/OFB/NULL",
new BigIntegerCounterNonce(BigInteger.ONE, 16),
},
new Object[] {
"src/test/resources/plaintexts/lorem-1200.txt",
"AES/CFB/PKCS5",
new RBGNonce(16),
},
new Object[] {
"src/test/resources/plaintexts/lorem-1200.txt",
"AES/ECB/PKCS5",
new RBGNonce(16),
},
};
}
@Test(dataProvider = "test-arrays")
public void testEncryptDecryptArray(final String input, final String cipherSpecString, final Nonce nonce)
throws Exception
{
final BufferedBlockCipherBean cipherBean = new BufferedBlockCipherBean();
final BufferedBlockCipherSpec cipherSpec = BufferedBlockCipherSpec.parse(cipherSpecString);
cipherBean.setNonce(nonce);
cipherBean.setKeyAlias("vtcrypt");
cipherBean.setKeyPassword("vtcrypt");
cipherBean.setKeyStore(getTestKeyStore());
cipherBean.setBlockCipherSpec(cipherSpec);
final byte[] ciphertext = cipherBean.encrypt(ByteUtil.toBytes(input));
assertEquals(ByteUtil.toString(cipherBean.decrypt(ciphertext)), input);
}
@Test(dataProvider = "test-streams")
public void testEncryptDecryptStream(final String path, final String cipherSpecString, final Nonce nonce)
throws Exception
{
final BufferedBlockCipherBean cipherBean = new BufferedBlockCipherBean();
final BufferedBlockCipherSpec cipherSpec = BufferedBlockCipherSpec.parse(cipherSpecString);
cipherBean.setNonce(nonce);
cipherBean.setKeyAlias("vtcrypt");
cipherBean.setKeyPassword("vtcrypt");
cipherBean.setKeyStore(getTestKeyStore());
cipherBean.setBlockCipherSpec(cipherSpec);
final ByteArrayOutputStream tempOut = new ByteArrayOutputStream(8192);
cipherBean.encrypt(StreamUtil.makeStream(new File(path)), tempOut);
final ByteArrayInputStream tempIn = new ByteArrayInputStream(tempOut.toByteArray());
final ByteArrayOutputStream finalOut = new ByteArrayOutputStream(8192);
cipherBean.decrypt(tempIn, finalOut);
assertEquals(ByteUtil.toString(finalOut.toByteArray()), ByteUtil.toString(StreamUtil.readAll(path)));
}
private static KeyStore getTestKeyStore()
{
final KeyStoreFactoryBean bean = new KeyStoreFactoryBean();
bean.setPassword("vtcrypt");
bean.setResource(new FileResource(new File("src/test/resources/keystores/cipher-bean.jceks")));
bean.setType("JCEKS");
return bean.newInstance();
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/EncodingHashBeanTest.java 0000664 0000000 0000000 00000006036 14226043123 0027411 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import org.cryptacular.FailListener;
import org.cryptacular.spec.CodecSpec;
import org.cryptacular.spec.DigestSpec;
import org.cryptacular.util.ByteUtil;
import org.cryptacular.util.CodecUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
* Unit test for {@link EncodingHashBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class EncodingHashBeanTest
{
@DataProvider(name = "hash-data")
public Object[][] getHashData()
{
return
new Object[][] {
{
new EncodingHashBean(CodecSpec.BASE64, new DigestSpec("SHA1"), 1, false),
new Object[] {
CodecUtil.b64("7FHsteHnm6XQsJT1TTKbxw=="),
CodecUtil.b64("ehp6PCnojSegFpRvStqQ9A=="),
},
"Oadnuuj7QsRPUuMBiu+dmlT6qzU=",
},
{
new EncodingHashBean(CodecSpec.BASE64, new DigestSpec("SHA1"), 1, true),
new Object[] {
CodecUtil.b64("7FHsteHnm6XQsJT1TTKbxw=="),
CodecUtil.b64("ehp6PCnojSegFpRvStqQ9A=="),
CodecUtil.b64("/siCJIPstwM="),
},
"uRt+VlmPzfGOPjSGoZLTxpvd1dP+yIIkg+y3Aw==",
},
{
new EncodingHashBean(CodecSpec.HEX, new DigestSpec("SHA256"), 3, false),
new Object[] {
CodecUtil.b64("7FHsteHnm6XQsJT1TTKbxw=="),
CodecUtil.b64("ehp6PCnojSegFpRvStqQ9A=="),
},
"3a1edec6aef6d1736bec63130755690c07f04d7e7139d8fd685cc2d989961b79",
},
{
new EncodingHashBean(CodecSpec.HEX, new DigestSpec("SHA256"), 3, true),
new Object[] {
CodecUtil.b64("7FHsteHnm6XQsJT1TTKbxw=="),
CodecUtil.b64("ehp6PCnojSegFpRvStqQ9A=="),
CodecUtil.b64("DH9M1lDibNU="),
},
"79f2868e7f72ed18cd67858e8ffe589c6090d696f7ff298e021faf5855fd41a10c7f4cd650e26cd5",
},
};
}
@DataProvider(name = "compare-data")
public Object[][] getCompareData()
{
return
new Object[][] {
{
new EncodingHashBean(CodecSpec.BASE64, new DigestSpec("SHA1"), 1, false),
"7fyOZXGp+gKMziV/2Px7RIMkxyI2O1H8",
new Object[] {ByteUtil.toBytes("password"), },
},
{
new EncodingHashBean(CodecSpec.BASE64, new DigestSpec("SHA1"), 1, true),
"lrb+YkKHqoGbFtxYd0B5567N6ZYwqwvWQwvoSg==",
new Object[] {ByteUtil.toBytes("password"), },
},
};
}
@Test(dataProvider = "hash-data")
public void testHash(final EncodingHashBean bean, final Object[] input, final String expected)
throws Exception
{
assertEquals(bean.hash(input), expected);
}
@Test(dataProvider = "compare-data")
public void testCompare(final EncodingHashBean bean, final String hash, final Object[] input)
throws Exception
{
assertTrue(bean.compare(hash, input));
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/KeyStoreBasedKeyFactoryBeanTest.java 0000664 0000000 0000000 00000004134 14226043123 0031561 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.io.File;
import java.security.Key;
import java.security.interfaces.RSAPrivateKey;
import javax.crypto.SecretKey;
import org.cryptacular.FailListener;
import org.cryptacular.io.FileResource;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link KeyStoreBasedKeyFactoryBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class KeyStoreBasedKeyFactoryBeanTest
{
private static final String KS_PATH = "src/test/resources/keystores/";
@DataProvider(name = "keys")
public Object[][] getKeys()
{
return
new Object[][] {
{
KS_PATH + "factory-bean.jceks",
"JCEKS",
"aes256",
"AES",
32,
},
{
KS_PATH + "factory-bean.jceks",
"JCEKS",
"rsa2048",
"RSA",
2048,
},
};
}
@Test(dataProvider = "keys")
public void testNewInstance(
final String keyStorePath,
final String keyStoreType,
final String alias,
final String expectedAlg,
final int expectedSize)
throws Exception
{
final KeyStoreFactoryBean keyStoreFactory = new KeyStoreFactoryBean();
keyStoreFactory.setResource(new FileResource(new File(keyStorePath)));
keyStoreFactory.setPassword("vtcrypt");
keyStoreFactory.setType(keyStoreType);
final KeyStoreBasedKeyFactoryBean secretKeyFactory = new KeyStoreBasedKeyFactoryBean();
secretKeyFactory.setKeyStore(keyStoreFactory.newInstance());
secretKeyFactory.setAlias(alias);
secretKeyFactory.setPassword("vtcrypt");
final Key key = secretKeyFactory.newInstance();
assertEquals(key.getAlgorithm(), expectedAlg);
if (key instanceof SecretKey) {
assertEquals(key.getEncoded().length, expectedSize);
} else if (key instanceof RSAPrivateKey) {
assertEquals(((RSAPrivateKey) key).getModulus().bitLength(), expectedSize);
}
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/KeyStoreFactoryBeanTest.java 0000664 0000000 0000000 00000003006 14226043123 0030146 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.io.File;
import org.cryptacular.FailListener;
import org.cryptacular.io.FileResource;
import org.cryptacular.io.Resource;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link KeyStoreFactoryBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class KeyStoreFactoryBeanTest
{
private static final String KS_PATH = "src/test/resources/keystores/";
@DataProvider(name = "keystore-data")
public Object[][] getKeyStoreData()
{
return
new Object[][] {
new Object[] {
"JCEKS",
new FileResource(new File(KS_PATH + "keystore.jceks")),
1,
},
new Object[] {
"JKS",
new FileResource(new File(KS_PATH + "keystore.jks")),
1,
},
new Object[] {
"PKCS12",
new FileResource(new File(KS_PATH + "keystore.p12")),
1,
},
};
}
@Test(dataProvider = "keystore-data")
public void testNewInstance(final String type, final Resource resource, final int expectedSize)
throws Exception
{
final KeyStoreFactoryBean factory = new KeyStoreFactoryBean();
factory.setType(type);
factory.setResource(resource);
factory.setPassword("vtcrypt");
assertEquals(factory.newInstance().size(), expectedSize);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/PemBasedPrivateKeyFactoryBeanTest.java 0000664 0000000 0000000 00000002640 14226043123 0032070 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.io.File;
import java.security.PrivateKey;
import org.cryptacular.FailListener;
import org.cryptacular.util.ByteUtil;
import org.cryptacular.util.StreamUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertTrue;
/**
* Unit test for {@link PemBasedPrivateKeyFactoryBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class PemBasedPrivateKeyFactoryBeanTest
{
private static final String KEY_PATH = "src/test/resources/keys/";
@DataProvider(name = "keys")
public Object[][] getKeys()
{
return
new Object[][] {
new Object[] {KEY_PATH + "dsa-pkcs8-nopass.pem"},
new Object[] {KEY_PATH + "dsa-openssl-nopass.pem"},
new Object[] {KEY_PATH + "rsa-pkcs8-nopass.pem"},
new Object[] {KEY_PATH + "rsa-openssl-nopass.pem"},
new Object[] {KEY_PATH + "ec-openssl-sect571r1-explicit-nopass.pem"},
};
}
@Test(dataProvider = "keys")
public void testNewInstance(final String path)
throws Exception
{
final String pem = ByteUtil.toString(StreamUtil.readAll(new File(path)));
final PemBasedPrivateKeyFactoryBean factory = new PemBasedPrivateKeyFactoryBean(pem);
assertTrue(factory.newInstance() instanceof PrivateKey);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/PemBasedPublicKeyFactoryBeanTest.java 0000664 0000000 0000000 00000002405 14226043123 0031673 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.io.File;
import java.security.PublicKey;
import org.cryptacular.FailListener;
import org.cryptacular.util.ByteUtil;
import org.cryptacular.util.StreamUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertTrue;
/**
* Unit test for {@link PemBasedPublicKeyFactoryBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class PemBasedPublicKeyFactoryBeanTest
{
private static final String KEY_PATH = "src/test/resources/keys/";
@DataProvider(name = "keys")
public Object[][] getKeys()
{
return
new Object[][] {
new Object[] {KEY_PATH + "dsa-pub.pem"},
new Object[] {KEY_PATH + "rsa-pub.pem"},
new Object[] {KEY_PATH + "ec-secp224k1-explicit-pub.pem"},
};
}
@Test(dataProvider = "keys")
public void testNewInstance(final String path)
throws Exception
{
final String pem = ByteUtil.toString(StreamUtil.readAll(new File(path)));
final PemBasedPublicKeyFactoryBean factory = new PemBasedPublicKeyFactoryBean(pem);
assertTrue(factory.newInstance() instanceof PublicKey);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/ResourceBasedPrivateKeyFactoryBeanTest.java 0000664 0000000 0000000 00000004022 14226043123 0033132 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.io.File;
import java.security.PrivateKey;
import org.cryptacular.FailListener;
import org.cryptacular.io.FileResource;
import org.cryptacular.io.Resource;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertTrue;
/**
* Unit test for {@link ResourceBasedPrivateKeyFactoryBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class ResourceBasedPrivateKeyFactoryBeanTest
{
private static final String KEY_PATH = "src/test/resources/keys/";
@DataProvider(name = "keys")
public Object[][] getKeys()
{
return
new Object[][] {
new Object[] {KEY_PATH + "dsa-pkcs8-nopass.pem", null},
new Object[] {KEY_PATH + "dsa-openssl-nopass.pem", null},
new Object[] {KEY_PATH + "rsa-pkcs8-nopass.pem", null},
new Object[] {KEY_PATH + "rsa-openssl-nopass.pem", null},
new Object[] {
KEY_PATH + "ec-openssl-sect571r1-explicit-nopass.pem",
null,
},
new Object[] {KEY_PATH + "dsa-openssl-des3.pem", "vtcrypt"},
new Object[] {KEY_PATH + "dsa-pkcs8-v2-des3.der", "vtcrypt"},
new Object[] {
KEY_PATH + "ec-pkcs8-sect571r1-explicit-v2-aes128.pem",
"vtcrypt",
},
new Object[] {
KEY_PATH + "ec-pkcs8-sect571r1-named-v1-sha1-rc2-64.der",
"vtcrypt",
},
new Object[] {KEY_PATH + "rsa-openssl-des.pem", "vtcrypt"},
new Object[] {KEY_PATH + "rsa-pkcs8-v2-aes256.der", "vtcrypt"},
};
}
@Test(dataProvider = "keys")
public void testNewInstance(final String path, final String password)
throws Exception
{
final Resource resource = new FileResource(new File(path));
final ResourceBasedPrivateKeyFactoryBean factory = new ResourceBasedPrivateKeyFactoryBean(resource, password);
assertTrue(factory.newInstance() instanceof PrivateKey);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/ResourceBasedPublicKeyFactoryBeanTest.java 0000664 0000000 0000000 00000002347 14226043123 0032746 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.security.PublicKey;
import org.cryptacular.FailListener;
import org.cryptacular.io.ClassPathResource;
import org.cryptacular.io.Resource;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertTrue;
/**
* Unit test for {@link ResourceBasedPublicKeyFactoryBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class ResourceBasedPublicKeyFactoryBeanTest
{
private static final String KEY_PATH = "/keys/";
@DataProvider(name = "keys")
public Object[][] getKeys()
{
return
new Object[][] {
new Object[] {KEY_PATH + "dsa-pub.pem"},
new Object[] {KEY_PATH + "rsa-pub.pem"},
new Object[] {KEY_PATH + "ec-secp224k1-explicit-pub.pem"},
};
}
@Test(dataProvider = "keys")
public void testNewInstance(final String path)
throws Exception
{
final Resource resource = new ClassPathResource(path);
final ResourceBasedPublicKeyFactoryBean factory = new ResourceBasedPublicKeyFactoryBean(resource);
assertTrue(factory.newInstance() instanceof PublicKey);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/ResourceBasedSecretKeyFactoryBeanTest.java 0000664 0000000 0000000 00000002400 14226043123 0032743 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.io.File;
import org.cryptacular.FailListener;
import org.cryptacular.io.FileResource;
import org.cryptacular.io.Resource;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link ResourceBasedSecretKeyFactoryBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class ResourceBasedSecretKeyFactoryBeanTest
{
private static final String KEY_PATH = "src/test/resources/keys/";
@DataProvider(name = "keys")
public Object[][] getKeys()
{
return new Object[][] {
new Object[] {
"AES",
new FileResource(new File(KEY_PATH + "aes-128.key")),
16,
},
};
}
@Test(dataProvider = "keys")
public void testNewInstance(final String algorithm, final Resource resource, final int expectedSize)
throws Exception
{
final ResourceBasedSecretKeyFactoryBean factory = new ResourceBasedSecretKeyFactoryBean();
factory.setAlgorithm(algorithm);
factory.setResource(resource);
assertEquals(factory.newInstance().getEncoded().length, expectedSize);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/bean/SimpleHashBeanTest.java 0000664 0000000 0000000 00000003023 14226043123 0027105 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import org.cryptacular.FailListener;
import org.cryptacular.spec.DigestSpec;
import org.cryptacular.util.CodecUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link SimpleHashBean}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class SimpleHashBeanTest
{
@DataProvider(name = "test-data")
public Object[][] getTestData()
{
return
new Object[][] {
{
new DigestSpec("SHA1"),
new Object[] {
CodecUtil.b64("7FHsteHnm6XQsJT1TTKbxw=="),
CodecUtil.b64("ehp6PCnojSegFpRvStqQ9A=="),
},
1,
"Oadnuuj7QsRPUuMBiu+dmlT6qzU=",
},
{
new DigestSpec("SHA256"),
new Object[] {
CodecUtil.b64("7FHsteHnm6XQsJT1TTKbxw=="),
CodecUtil.b64("ehp6PCnojSegFpRvStqQ9A=="),
},
3,
"Oh7exq720XNr7GMTB1VpDAfwTX5xOdj9aFzC2YmWG3k=",
},
};
}
@Test(dataProvider = "test-data")
public void testHash(final DigestSpec digest, final Object[] input, final int iterations, final String expectedBase64)
throws Exception
{
final SimpleHashBean bean = new SimpleHashBean();
bean.setDigestSpec(digest);
bean.setIterations(iterations);
assertEquals(CodecUtil.b64(bean.hash(input)), expectedBase64);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/codec/ 0000775 0000000 0000000 00000000000 14226043123 0022731 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/test/java/org/cryptacular/codec/Base32DecoderTest.java 0000664 0000000 0000000 00000004206 14226043123 0026743 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.FailListener;
import org.cryptacular.util.ByteUtil;
import org.cryptacular.util.CodecUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link Base64Decoder}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class Base32DecoderTest
{
@DataProvider(name = "encoded-data")
public Object[][] getEncodedData()
{
final Base32Decoder unpadded = new Base32Decoder();
unpadded.setPaddedInput(false);
return
new Object[][] {
// Multiple of 40 bits
new Object[] {
new Base32Decoder(),
"TQSN7XJ4",
CodecUtil.hex("9c24dfdd3c"),
},
// Final quantum of encoding input is exactly 8 bits
new Object[] {
unpadded,
"43H7CNN2EI",
CodecUtil.hex("e6cff135ba22"),
},
// Final quantum of encoding input is exactly 16 bits
new Object[] {
new Base32Decoder(),
"2NEK2FDJHXDQ====",
CodecUtil.hex("d348ad14693dc7"),
},
// Final quantum of encoding input is exactly 24 bits
new Object[] {
new Base32Decoder(),
"LVVECZIT6F3MU===",
CodecUtil.hex("5d6a416513f176ca"),
},
// Final quantum of encoding input is exactly 32 bits
new Object[] {
new Base32Decoder(),
"QN5Z7HN4PBY4G5Q=",
CodecUtil.hex("837b9f9dbc7871c376"),
},
};
}
@Test(dataProvider = "encoded-data")
public void testDecode(final Base32Decoder decoder, final String data, final byte[] expected)
throws Exception
{
final CharBuffer input = CharBuffer.wrap(data);
final ByteBuffer output = ByteBuffer.allocate(decoder.outputSize(input.length()));
decoder.decode(input, output);
decoder.finalize(output);
output.flip();
assertEquals(ByteUtil.toArray(output), expected);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/codec/Base32EncoderTest.java 0000664 0000000 0000000 00000004115 14226043123 0026754 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.FailListener;
import org.cryptacular.util.CodecUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link Base64Encoder}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class Base32EncoderTest
{
@DataProvider(name = "byte-data")
public Object[][] getByteData()
{
final Base32Encoder unpadded = new Base32Encoder();
unpadded.setPaddedOutput(false);
return
new Object[][] {
// Multiple of 40 bits
new Object[] {
new Base32Encoder(),
CodecUtil.hex("9c24dfdd3c"),
"TQSN7XJ4",
},
// Final quantum of encoding input is exactly 8 bits
new Object[] {
new Base32Encoder(),
CodecUtil.hex("e6cff135ba22"),
"43H7CNN2EI======",
},
// Final quantum of encoding input is exactly 16 bits
new Object[] {
new Base32Encoder(),
CodecUtil.hex("d348ad14693dc7"),
"2NEK2FDJHXDQ====",
},
// Final quantum of encoding input is exactly 24 bits
new Object[] {
unpadded,
CodecUtil.hex("5d6a416513f176ca"),
"LVVECZIT6F3MU",
},
// Final quantum of encoding input is exactly 32 bits
new Object[] {
new Base32Encoder(),
CodecUtil.hex("837b9f9dbc7871c376"),
"QN5Z7HN4PBY4G5Q=",
},
};
}
@Test(dataProvider = "byte-data")
public void testEncode(final Base32Encoder encoder, final byte[] inBytes, final String expected)
throws Exception
{
final ByteBuffer input = ByteBuffer.wrap(inBytes);
final CharBuffer output = CharBuffer.allocate(encoder.outputSize(input.limit()));
encoder.encode(input, output);
encoder.finalize(output);
assertEquals(output.flip().toString(), expected);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/codec/Base64DecoderTest.java 0000664 0000000 0000000 00000007214 14226043123 0026752 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
import java.io.File;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.FailListener;
import org.cryptacular.util.ByteUtil;
import org.cryptacular.util.CodecUtil;
import org.cryptacular.util.HashUtil;
import org.cryptacular.util.StreamUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link Base64Decoder} class.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class Base64DecoderTest
{
@DataProvider(name = "encoded-data")
public Object[][] getEncodedData()
{
return
new Object[][] {
new Object[] {
new Base64Decoder(),
"QWJsZSB3YXMgSSBlcmUgSSBzYXcgZWxiYQ==",
ByteUtil.toBytes("Able was I ere I saw elba"),
},
new Object[] {
new Base64Decoder(),
"QWJsZSB3YXMgSSBlcmUgSSBzYXcgZWxiYS4=",
ByteUtil.toBytes("Able was I ere I saw elba."),
},
new Object[] {
new Base64Decoder(),
"safx/LW8+SsSy/o3PmCNy4VEm5s=",
HashUtil.sha1(ByteUtil.toBytes("t3stUs3r01")),
},
new Object[] {
new Base64Decoder.Builder().setUrlSafe(true).build(),
"safx_LW8-SsSy_o3PmCNy4VEm5s=",
HashUtil.sha1(ByteUtil.toBytes("t3stUs3r01")),
},
new Object[] {
new Base64Decoder.Builder().setUrlSafe(true).setPadding(false).build(),
"FPu_A9l-",
CodecUtil.hex("14FBBF03D97E"),
},
new Object[] {
new Base64Decoder.Builder().setUrlSafe(true).setPadding(false).build(),
"FPu_A9k",
CodecUtil.hex("14FBBF03D9"),
},
};
}
@DataProvider(name = "plaintext-files")
public Object[][] getPlaintextFiles()
{
return
new Object[][] {
new Object[] {"src/test/resources/plaintexts/lorem-1190.txt"},
new Object[] {"src/test/resources/plaintexts/lorem-1200.txt"},
new Object[] {"src/test/resources/plaintexts/lorem-5000.txt"},
};
}
@Test(dataProvider = "encoded-data")
public void testDecode(final Base64Decoder decoder, final String data, final byte[] expected)
throws Exception
{
final CharBuffer input = CharBuffer.wrap(data);
final ByteBuffer output = ByteBuffer.allocate(decoder.outputSize(input.length()));
decoder.decode(input, output);
decoder.finalize(output);
output.flip();
assertEquals(ByteUtil.toArray(output), expected);
}
@Test(dataProvider = "plaintext-files")
public void testDecodeFile(final String path)
throws Exception
{
final String expected = StreamUtil.readAll(StreamUtil.makeReader(new File(path)));
final File file = new File(path + ".b64");
final StringBuilder actual = new StringBuilder(expected.length());
final Reader reader = StreamUtil.makeReader(file);
final Base64Decoder decoder = new Base64Decoder();
try {
final CharBuffer bufIn = CharBuffer.allocate(1024);
final ByteBuffer bufOut = ByteBuffer.allocate(decoder.outputSize(bufIn.capacity()));
while (reader.read(bufIn) > 0) {
bufIn.flip();
decoder.decode(bufIn, bufOut);
bufOut.flip();
actual.append(ByteUtil.toCharBuffer(bufOut));
bufOut.clear();
bufIn.clear();
}
decoder.finalize(bufOut);
bufOut.flip();
actual.append(ByteUtil.toCharBuffer(bufOut));
} finally {
StreamUtil.closeReader(reader);
}
assertEquals(actual.toString(), expected);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/codec/Base64EncoderTest.java 0000664 0000000 0000000 00000010221 14226043123 0026754 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import org.cryptacular.FailListener;
import org.cryptacular.util.ByteUtil;
import org.cryptacular.util.CodecUtil;
import org.cryptacular.util.HashUtil;
import org.cryptacular.util.StreamUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link Base64Encoder} class.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class Base64EncoderTest
{
@DataProvider(name = "byte-data")
public Object[][] getByteData()
{
final Base64Encoder unpadded = new Base64Encoder();
unpadded.setPaddedOutput(false);
return
new Object[][] {
new Object[] {
new Base64Encoder(),
ByteUtil.toBytes("Able was I ere I saw elba"),
"QWJsZSB3YXMgSSBlcmUgSSBzYXcgZWxiYQ==",
},
new Object[] {
new Base64Encoder.Builder().setPadding(false).build(),
ByteUtil.toBytes("Able was I ere I saw elba"),
"QWJsZSB3YXMgSSBlcmUgSSBzYXcgZWxiYQ",
},
new Object[] {
new Base64Encoder(),
ByteUtil.toBytes("Able was I ere I saw elba."),
"QWJsZSB3YXMgSSBlcmUgSSBzYXcgZWxiYS4=",
},
new Object[] {
new Base64Encoder(),
HashUtil.sha1(ByteUtil.toBytes("t3stUs3r01")),
"safx/LW8+SsSy/o3PmCNy4VEm5s=",
},
new Object[] {
new Base64Encoder(true),
HashUtil.sha1(ByteUtil.toBytes("t3stUs3r01")),
"safx_LW8-SsSy_o3PmCNy4VEm5s=",
},
new Object[] {
new Base64Encoder(),
CodecUtil.hex("3f1c435a244f7a8be1572a1bf2a196f4958cc00c17b96e"),
"PxxDWiRPeovhVyob8qGW9JWMwAwXuW4=",
},
new Object[] {
new Base64Encoder.Builder().setUrlSafe(true).setPadding(false).build(),
CodecUtil.hex("14FBBF03D97E"),
"FPu_A9l-",
},
new Object[] {
new Base64Encoder.Builder().setUrlSafe(true).setPadding(false).build(),
CodecUtil.hex("14FBBF03D9"),
"FPu_A9k",
},
};
}
@DataProvider(name = "plaintext-files")
public Object[][] getPlaintextFiles()
{
return
new Object[][] {
new Object[] {"src/test/resources/plaintexts/lorem-1190.txt"},
new Object[] {"src/test/resources/plaintexts/lorem-1200.txt"},
new Object[] {"src/test/resources/plaintexts/lorem-5000.txt"},
};
}
@Test(dataProvider = "byte-data")
public void testEncode(final Base64Encoder encoder, final byte[] inBytes, final String expected)
throws Exception
{
final ByteBuffer input = ByteBuffer.wrap(inBytes);
final CharBuffer output = CharBuffer.allocate(encoder.outputSize(input.limit()));
encoder.encode(input, output);
encoder.finalize(output);
assertEquals(output.flip().toString(), expected);
}
@Test(dataProvider = "plaintext-files")
public void testEncodeFile(final String path)
throws Exception
{
final File file = new File(path);
String expectedPath = path + ".b64";
if ("\r\n".equals(System.lineSeparator())) {
expectedPath += ".crlf";
}
final String expected = new String(StreamUtil.readAll(expectedPath));
final StringBuilder actual = new StringBuilder(expected.length());
final Base64Encoder encoder = new Base64Encoder(64);
try (FileInputStream input = new FileInputStream(file)) {
final ByteBuffer bufIn = ByteBuffer.allocate(512);
final CharBuffer bufOut = CharBuffer.allocate(encoder.outputSize(512));
final FileChannel chIn = input.getChannel();
while (chIn.read(bufIn) > 0) {
bufIn.flip();
encoder.encode(bufIn, bufOut);
bufOut.flip();
actual.append(bufOut);
bufOut.clear();
bufIn.clear();
}
encoder.finalize(bufOut);
bufOut.flip();
actual.append(bufOut);
}
assertEquals(actual.toString(), expected);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/codec/HexDecoderTest.java 0000664 0000000 0000000 00000004012 14226043123 0026443 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.FailListener;
import org.cryptacular.util.ByteUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link HexDecoder} class.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class HexDecoderTest
{
@DataProvider(name = "hex-data")
public Object[][] getHexData()
{
return
new Object[][] {
new Object[] {
"41626C652077617320492065726520492073617720656C6261",
"Able was I ere I saw elba",
},
new Object[] {
"41626c652 077617320492065726520492073617720656c626\n1",
"Able was I ere I saw elba",
},
new Object[] {
"41626c652 077617320492065726520492073617720656c626\n",
"Able was I ere I saw elb",
},
new Object[] {
"41:62:6c:65:20:77:61:73:20:49:20:65:72:65:20:49:20:73:61:77:20:65:6c:62:61",
"Able was I ere I saw elba",
},
new Object[] {
"9c63b0547798b60d5e04",
ByteUtil.toString(
new byte[] {
(byte) -100,
(byte) 99,
(byte) -80,
(byte) 84,
(byte) 119,
(byte) -104,
(byte) -74,
(byte) 13,
(byte) 94,
(byte) 4,
}),
},
};
}
@Test(dataProvider = "hex-data")
public void testDecode(final String encoded, final String expected)
throws Exception
{
final HexDecoder decoder = new HexDecoder();
final ByteBuffer output = ByteBuffer.allocate(decoder.outputSize(encoded.length()));
decoder.decode(CharBuffer.wrap(encoded), output);
decoder.finalize(output);
output.flip();
assertEquals(ByteUtil.toString(output), expected);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/codec/HexEncoderTest.java 0000664 0000000 0000000 00000004253 14226043123 0026464 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.codec;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.FailListener;
import org.cryptacular.util.ByteUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link HexEncoder} class.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class HexEncoderTest
{
@DataProvider(name = "text-data")
public Object[][] getTextData()
{
return
new Object[][] {
new Object[] {
new HexEncoder(false),
ByteUtil.toBytes("Able was I ere I saw elba"),
"41626c652077617320492065726520492073617720656c6261",
},
new Object[] {
new HexEncoder(false, true),
ByteUtil.toBytes("Able was I ere I saw elba\n"),
"41626C652077617320492065726520492073617720656C62610A",
},
new Object[] {
new HexEncoder(true),
ByteUtil.toBytes("Able was I ere I saw elba"),
"41:62:6c:65:20:77:61:73:20:49:20:65:72:65:20:49:20:73:61:77:20:65:6c:62:61",
},
new Object[] {
new HexEncoder(true, true),
ByteUtil.toBytes("Able was I ere I saw elba"),
"41:62:6C:65:20:77:61:73:20:49:20:65:72:65:20:49:20:73:61:77:20:65:6C:62:61",
},
new Object[] {
new HexEncoder(),
new byte[] {
(byte) -100,
(byte) 99,
(byte) -80,
(byte) 84,
(byte) 119,
(byte) -104,
(byte) -74,
(byte) 13,
(byte) 94,
(byte) 4,
},
"9c63b0547798b60d5e04",
},
};
}
@Test(dataProvider = "text-data")
public void testEncode(final HexEncoder encoder, final byte[] data, final String expected)
throws Exception
{
final CharBuffer output = CharBuffer.allocate(encoder.outputSize(data.length));
encoder.encode(ByteBuffer.wrap(data), output);
encoder.finalize(output);
assertEquals(output.flip().toString(), expected);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/generator/ 0000775 0000000 0000000 00000000000 14226043123 0023642 5 ustar 00root root 0000000 0000000 cryptacular-1.2.5/src/test/java/org/cryptacular/generator/HOTPGeneratorTest.java 0000664 0000000 0000000 00000003035 14226043123 0027767 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
import org.cryptacular.FailListener;
import org.cryptacular.util.CodecUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link HOTPGenerator}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class HOTPGeneratorTest
{
@DataProvider(name = "test-data")
public Object[][] getTestData()
{
return
new Object[][] {
{"0x3132333435363738393031323334353637383930", 0, 755224},
{"0x3132333435363738393031323334353637383930", 1, 287082},
{"0x3132333435363738393031323334353637383930", 2, 359152},
{"0x3132333435363738393031323334353637383930", 3, 969429},
{"0x3132333435363738393031323334353637383930", 4, 338314},
{"0x3132333435363738393031323334353637383930", 5, 254676},
{"0x3132333435363738393031323334353637383930", 6, 287922},
{"0x3132333435363738393031323334353637383930", 7, 162583},
{"0x3132333435363738393031323334353637383930", 8, 399871},
{"0x3132333435363738393031323334353637383930", 9, 520489},
};
}
@Test(dataProvider = "test-data")
public void testGenerate(final String hexKey, final int count, final int expected)
throws Exception
{
final HOTPGenerator generator = new HOTPGenerator();
assertEquals(generator.generate(CodecUtil.hex(hexKey), count), expected);
}
}
cryptacular-1.2.5/src/test/java/org/cryptacular/generator/RandomIdGeneratorTest.java 0000664 0000000 0000000 00000006522 14226043123 0030716 0 ustar 00root root 0000000 0000000 /* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.cryptacular.FailListener;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
/**
* Unit test for {@link RandomIdGenerator}.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class RandomIdGeneratorTest
{
@DataProvider(name = "generators")
public Object[][] getGenerators()
{
return
new Object[][] {
{
new RandomIdGenerator(10),
Pattern.compile("\\w{10}"),
},
{
new RandomIdGenerator(128),
Pattern.compile("\\w{128}"),
},
{
new RandomIdGenerator(20, "abcdefg"),
Pattern.compile("[abcdefg]{20}"),
},
};
}
@Test(dataProvider = "generators")
public void testGenerate(final RandomIdGenerator generator, final Pattern expected)
{
for (int i = 0; i < 100; i++) {
final Matcher m = expected.matcher(generator.generate());
assertTrue(m.matches());
}
}
/**
* Test concurrent random ID generation on a shared instance.
*
* @throws Exception on test errors
*/
@Test
public void testConcurrentGeneration()
throws Exception
{
final int poolSize = 100;
final ExecutorService executor = Executors.newFixedThreadPool(poolSize);
final RandomIdGenerator generator = new RandomIdGenerator(50);
final Collection