org.picketlink.json.jose.crypto.AESGCM Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.picketlink.json.jose.crypto;
import java.security.SecureRandom;
import javax.crypto.SecretKey;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
/**
* AES/GSM/NoPadding encryption and decryption methods.
*
*
* See draft-ietf-jose-json-web-algorithms-26, section 5.1 and appendix 3.
*
* @author Giriraj Sharma
*/
public class AESGCM {
/**
* The standard Initialization Vector (IV) length (96 bits).
*/
public static final int IV_BIT_LENGTH = 96;
/**
* The standard authentication tag length (128 bits).
*/
public static final int AUTH_TAG_BIT_LENGTH = 128;
/**
* Generates a random 96 bit (12 byte) Initialization Vector(IV) for use in AES-GCM encryption.
*
*
* See draft-ietf-jose-json-web-algorithms-26, section 5.3.
*
* @param randomGen The secure random generator to use. Must be correctly initialized and not {@code null}.
*
* @return The random 96 bit IV, as 12 byte array.
*/
public static byte[] generateIV(final SecureRandom randomGen) {
byte[] bytes = new byte[IV_BIT_LENGTH / 8];
randomGen.nextBytes(bytes);
return bytes;
}
/**
* Creates a new AES/GCM/NoPadding cipher.
*
* @param secretKey The AES key. Must not be {@code null}.
* @param forEncryption If {@code true} creates an encryption cipher, else creates a decryption cipher.
* @param iv The initialisation vector (IV). Must not be {@code null}.
* @param authData The authenticated data. Must not be {@code null}.
*
* @return The AES/GCM/NoPadding cipher.
*/
private static GCMBlockCipher createAESGCMCipher(final SecretKey secretKey,
final boolean forEncryption,
final byte[] iv,
final byte[] authData) {
// Initialize AES cipher
BlockCipher cipher = AES.createCipher(secretKey, forEncryption);
// Create GCM cipher with AES
GCMBlockCipher gcm = new GCMBlockCipher(cipher);
AEADParameters aeadParams = new AEADParameters(new KeyParameter(secretKey.getEncoded()),
AUTH_TAG_BIT_LENGTH,
iv,
authData);
gcm.init(forEncryption, aeadParams);
return gcm;
}
/**
* Encrypts the specified plain text using AES/GCM/NoPadding.
*
* @param secretKey The AES key. Must not be {@code null}.
* @param plainText The plain text. Must not be {@code null}.
* @param iv The initialization vector (IV). Must not be {@code null}.
* @param authData The authenticated data. Must not be {@code null}.
*
* @return The authenticated cipher text.
*
* @throws RuntimeException If encryption failed.
*/
public static AuthenticatedCipherText encrypt(final SecretKey secretKey,
final byte[] iv,
final byte[] plainText,
final byte[] authData) {
// Initialise AES/GCM cipher for encryption
GCMBlockCipher cipher = createAESGCMCipher(secretKey, true, iv, authData);
// Prepare output buffer
int outputLength = cipher.getOutputSize(plainText.length);
byte[] output = new byte[outputLength];
// Produce cipher text
int outputOffset = cipher.processBytes(plainText, 0, plainText.length, output, 0);
// Produce authentication tag
try {
outputOffset += cipher.doFinal(output, outputOffset);
} catch (InvalidCipherTextException e) {
throw new RuntimeException("Couldn't generate GCM authentication tag: " + e.getMessage(), e);
}
// Split output into cipher text and authentication tag
int authTagLength = AUTH_TAG_BIT_LENGTH / 8;
byte[] cipherText = new byte[outputOffset - authTagLength];
byte[] authTag = new byte[authTagLength];
System.arraycopy(output, 0, cipherText, 0, cipherText.length);
System.arraycopy(output, outputOffset - authTagLength, authTag, 0, authTag.length);
return new AuthenticatedCipherText(cipherText, authTag);
}
/**
* Decrypts the specified cipher text using AES/GCM/NoPadding.
*
* @param secretKey The AES key. Must not be {@code null}.
* @param iv The initialisation vector (IV). Must not be {@code null}.
* @param cipherText The cipher text. Must not be {@code null}.
* @param authData The authenticated data. Must not be {@code null}.
* @param authTag The authentication tag. Must not be {@code null}.
*
* @return The decrypted plain text.
*
* @throws RuntimeException If decryption failed.
*/
public static byte[] decrypt(final SecretKey secretKey,
final byte[] iv,
final byte[] cipherText,
final byte[] authData,
final byte[] authTag) {
// Initialise AES/GCM cipher for decryption
GCMBlockCipher cipher = createAESGCMCipher(secretKey, false, iv, authData);
// Join cipher text and authentication tag to produce cipher input
byte[] input = new byte[cipherText.length + authTag.length];
System.arraycopy(cipherText, 0, input, 0, cipherText.length);
System.arraycopy(authTag, 0, input, cipherText.length, authTag.length);
int outputLength = cipher.getOutputSize(input.length);
byte[] output = new byte[outputLength];
// Decrypt
int outputOffset = cipher.processBytes(input, 0, input.length, output, 0);
// Validate authentication tag
try {
outputOffset += cipher.doFinal(output, outputOffset);
} catch (InvalidCipherTextException e) {
throw new RuntimeException("Couldn't validate GCM authentication tag: " + e.getMessage(), e);
}
return output;
}
/**
* Prevents public instantiation.
*/
private AESGCM() {
}
}