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

org.cryptacular.CiphertextHeader Maven / Gradle / Ivy

There is a newer version: 1.2.7
Show newest version
/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular;

import java.io.IOException;
import java.io.InputStream;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.cryptacular.util.ByteUtil;

/**
 * Cleartext header prepended to ciphertext providing data required for decryption.
 *
 * 

Data format:

* *
     +-----+----------+-------+------------+---------+
     | Len | NonceLen | Nonce | KeyNameLen | KeyName |
     +-----+----------+-------+------------+---------+
 * 
* *

Where fields are defined as follows:

* *
    *
  • Len - Total header length in bytes (4-byte integer)
  • *
  • NonceLen - Nonce length in bytes (4-byte integer)
  • *
  • Nonce - Nonce bytes (variable length)
  • *
  • KeyNameLen (OPTIONAL) - Key name length in bytes (4-byte integer)
  • *
  • KeyName (OPTIONAL) - Key name encoded as bytes in platform-specific encoding (variable length)
  • *
* *

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 */ public class CiphertextHeader { /** Header nonce field value. */ private final byte[] nonce; /** Header key name field value. */ private String keyName; /** Header length in bytes. */ private 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) { this.nonce = nonce; this.length = 8 + nonce.length; if (keyName != null) { this.length += 4 + keyName.getBytes().length; this.keyName = keyName; } } /** * 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(); } /** * 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("Invalid ciphertext header length: " + length); } final byte[] nonce; int nonceLen = 0; try { nonceLen = bb.getInt(); nonce = new byte[nonceLen]; bb.get(nonce); } catch (IndexOutOfBoundsException | BufferUnderflowException e) { throw new EncodingException("Invalid nonce length: " + nonceLen); } String keyName = null; if (length > nonce.length + 8) { final byte[] b; int keyLen = 0; try { keyLen = bb.getInt(); b = new byte[keyLen]; bb.get(b); keyName = new String(b); } catch (IndexOutOfBoundsException | BufferUnderflowException e) { throw new EncodingException("Invalid key length: " + keyLen); } } 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("Invalid ciphertext header length: " + length); } final byte[] nonce; int nonceLen = 0; try { nonceLen = ByteUtil.readInt(input); nonce = new byte[nonceLen]; input.read(nonce); } catch (ArrayIndexOutOfBoundsException e) { throw new EncodingException("Invalid nonce length: " + nonceLen); } 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); b = new byte[keyLen]; input.read(b); } catch (ArrayIndexOutOfBoundsException e) { throw new EncodingException("Invalid key length: " + keyLen); } catch (IOException e) { throw new StreamException(e); } keyName = new String(b); } return new CiphertextHeader(nonce, keyName); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy