
uk.ac.ceh.components.tokengeneration.stateless.StatelessTokenGenerator Maven / Gradle / Ivy
The newest version!
package uk.ac.ceh.components.tokengeneration.stateless;
import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Calendar;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import uk.ac.ceh.components.tokengeneration.ExpiredTokenException;
import uk.ac.ceh.components.tokengeneration.InvalidTokenException;
import uk.ac.ceh.components.tokengeneration.Token;
import uk.ac.ceh.components.tokengeneration.TokenGenerator;
/**
* The following TokenGenerator will use simple cryptographic principles to
* generate tokens which do not require state to be stored about those tokens.
*
* The only 2 pieces of information needed to release a token are an Hmac key and
* an AES key (Obtained from an instance of StatelessTokenKeyContainer)
* @author Christopher Johnson
*/
public class StatelessTokenGenerator implements TokenGenerator {
protected static final int INITALIZATION_VECTOR_SIZE = 16;
protected static final String MAC_ALGORITHM = "HmacSHA256";
protected static final String SECRET_KEY_ALGORITHM = "AES";
protected static final String CRYPTO_ALGORITHM = "AES/CBC/PKCS5Padding";
private StatelessTokenKeyContainer keys;
public StatelessTokenGenerator(StatelessTokenKeyContainer keys) {
this.keys = keys;
}
/**
* The following method will perform a secret message decryption of the given
* token.
* @param token The token to decrypt
* @return The message encoded in the Token which was encoded by this
* generator
* @throws InvalidTokenException If the token was not generated by this Object
* @throws ExpiredTokenException If the token is no longer valid
*/
@Override
public byte[] getMessage(Token token) throws InvalidTokenException, ExpiredTokenException {
try {
Mac mac = Mac.getInstance(MAC_ALGORITHM);
mac.init(keys.getHMacKey());
if(token.getBytes().length > INITALIZATION_VECTOR_SIZE + mac.getMacLength()) {
ByteBuffer message = ByteBuffer.wrap(token.getBytes());
byte[] keyCheckValue = getBytes(message, mac.getMacLength());
message.mark(); //store the current place into the buffer
//check if this message was created by this authenticator
if(Arrays.equals(keyCheckValue, mac.doFinal(getBytes(message, message.remaining())))) {
message.reset(); //return to just after reading the mac message
Cipher decrypt = Cipher.getInstance(CRYPTO_ALGORITHM);
decrypt.init(Cipher.DECRYPT_MODE, keys.getKey(), new IvParameterSpec( getBytes(message, INITALIZATION_VECTOR_SIZE) ));
ByteBuffer payload = ByteBuffer.wrap(decrypt.doFinal(getBytes(message, message.remaining())));
if(payload.getLong() >= Calendar.getInstance().getTimeInMillis()) //is ticket still valid
return getBytes(payload, payload.remaining());
else
throw new ExpiredTokenException("No longer valid");
}
}
throw new InvalidTokenException("Invalid composite key");
} catch (NoSuchAlgorithmException | NoSuchPaddingException ex) {
throw new RuntimeException("A configuration error has occurred", ex);
} catch ( InvalidKeyException | InvalidAlgorithmParameterException |
IllegalBlockSizeException | BadPaddingException bpe) {
throw new InvalidTokenException("Invalid composite key");
}
}
/**
* The following method will perform a SECRET_KEY_ALGORITHM encryption of
* the following structure.
*
* The byte structure of a token generated by this class is :
* BYTE_ARRAY_LENGTH | CONTENT
* -----------------------------------------------------------------------
* ---------------------------- PLAINTEXT --------------------------------
* 32 (byte size of hmac) | HMAC value of the message including
* | the plain text vector
* @see #INITALIZATION_VECTOR_SIZE | The vector used for the message
* --------------------------- CIPHER TEXT -------------------------------
* 8 (byte size of long) | The time in milliseconds from epoch
* | when this token expires
* message.length | A secret message to be encrypted in
* | the token
*
* @param message A message to be encrypted as a token
* @param ttl Time in milliseconds until this token expires
* @return An Token object with the above byte structure encrypted in
* @see #SECRET_KEY_ALGORITHM
*/
@Override
public Token generateToken(byte[] message, int ttl) {
try {
Cipher encrypt = Cipher.getInstance(CRYPTO_ALGORITHM);
encrypt.init(Cipher.ENCRYPT_MODE, keys.getKey());
Mac mac = Mac.getInstance(MAC_ALGORITHM);
mac.init(keys.getHMacKey());
byte[] cipherText = encrypt.doFinal(ByteBuffer.allocate(8 + message.length) //Encrypt byte buffer
.putLong(Calendar.getInstance().getTimeInMillis() + ttl) //Store the time when this token becomes invalid
.put(message) //Store message
.array()
);
byte[] payload = ByteBuffer.allocate(INITALIZATION_VECTOR_SIZE + cipherText.length)
.put(encrypt.getIV())
.put(cipherText)
.array();
return new Token(ByteBuffer.allocate(mac.getMacLength() + payload.length)
.put(mac.doFinal(payload))
.put(payload)
.array()
);
} catch ( IllegalBlockSizeException | BadPaddingException |
InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException ex) {
throw new RuntimeException("A configuration error has occurred", ex);
}
}
/**
* Utility method to read a byte array of size from a ByteBuffer
* @param buf The buffer to read from
* @param size The amount of bytes to read
* @return A byte array of bytes read from the buffer
*/
private static byte[] getBytes(ByteBuffer buf, int size) {
byte[] toReturn = new byte[size];
buf.get(toReturn);
return toReturn;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy