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

com.nimbusds.jose.crypto.impl.AESGCM Maven / Gradle / Ivy

Go to download

Java library for Javascript Object Signing and Encryption (JOSE) and JSON Web Tokens (JWT)

There is a newer version: 9.47
Show newest version
/*
 * nimbus-jose-jwt
 *
 * Copyright 2012-2024, Connect2id Ltd and contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the
 * License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.nimbusds.jose.crypto.impl;


import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.util.ByteUtils;
import com.nimbusds.jose.util.Container;
import com.nimbusds.jose.util.KeyUtils;
import net.jcip.annotations.ThreadSafe;

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;


/**
 * AES/GSM/NoPadding encryption and decryption methods. Falls back to the
 * BouncyCastle.org provider on Java 6. This class is thread-safe.
 *
 * 

See RFC 7518 (JWA), section 5.1 and appendix 3. * * @author Vladimir Dzhuvinov * @author Axel Nennker * @author Dimitar A. Stoikov * @version 2024-01-01 */ @ThreadSafe public class AESGCM { /** * The standard Initialisation 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) Initialisation Vector(IV) for * use in AES-GCM encryption. * *

See RFC 7518 (JWA), section 5.3. * * @param randomGen The secure random generator to use. Must be * correctly initialised 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; } /** * Encrypts the specified plain text using AES/GCM/NoPadding. * * @param secretKey The AES key. Must not be {@code null}. * @param ivContainer The initialisation vector (IV). Must not be * {@code null}. This is both input and output * parameter. On input, it carries externally * generated IV; on output, it carries the IV the * cipher actually used. JCA/JCE providers may * prefer to use an internally generated IV, e.g. as * described in * NIST * Special Publication 800-38D . * @param plainText The plain text. Must not be {@code null}. * @param authData The authenticated data. Must not be {@code null}. * @param provider The JCA provider to use, {@code null} implies the * default. * * @return The authenticated cipher text. * * @throws JOSEException If encryption failed. */ public static AuthenticatedCipherText encrypt(final SecretKey secretKey, final Container ivContainer, final byte[] plainText, final byte[] authData, final Provider provider) throws JOSEException { // Key alg must be "AES" final SecretKey aesKey = KeyUtils.toAESKey(secretKey); Cipher cipher; byte[] iv = ivContainer.get(); try { if (provider != null) { cipher = Cipher.getInstance("AES/GCM/NoPadding", provider); } else { cipher = Cipher.getInstance("AES/GCM/NoPadding"); } GCMParameterSpec gcmSpec = new GCMParameterSpec(AUTH_TAG_BIT_LENGTH, iv); cipher.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) { throw new JOSEException("Couldn't create AES/GCM/NoPadding cipher: " + e.getMessage(), e); } cipher.updateAAD(authData); byte[] cipherOutput; try { cipherOutput = cipher.doFinal(plainText); } catch (IllegalBlockSizeException | BadPaddingException e) { throw new JOSEException("Couldn't encrypt with AES/GCM/NoPadding: " + e.getMessage(), e); } final int tagPos = cipherOutput.length - ByteUtils.byteLength(AUTH_TAG_BIT_LENGTH); byte[] cipherText = ByteUtils.subArray(cipherOutput, 0, tagPos); byte[] authTag = ByteUtils.subArray(cipherOutput, tagPos, ByteUtils.byteLength(AUTH_TAG_BIT_LENGTH)); // retrieve the actual IV used by the cipher -- it may be internally-generated. ivContainer.set(actualIVOf(cipher)); return new AuthenticatedCipherText(cipherText, authTag); } /** * Retrieves the actual algorithm parameters and validates them. * * @param cipher The cipher to interrogate for the parameters it * actually used. * * @return The IV used by the specified cipher. * * @throws JOSEException If retrieval of the algorithm parameters from * the cipher failed, or the parameters are * deemed unusable. * * @see #actualParamsOf(Cipher) * @see #validate(byte[], int) */ private static byte[] actualIVOf(final Cipher cipher) throws JOSEException { GCMParameterSpec actualParams = actualParamsOf(cipher); byte[] iv = actualParams.getIV(); int tLen = actualParams.getTLen(); validate(iv, tLen); return iv; } /** * Validates the specified IV and authentication tag according to the * AES GCM requirements in * JWA RFC. * * @param iv The IV to check for compliance. * @param authTagLength The authentication tag length to check for * compliance. * * @throws JOSEException If the parameters don't match the JWA * requirements. * * @see #IV_BIT_LENGTH * @see #AUTH_TAG_BIT_LENGTH */ private static void validate(final byte[] iv, final int authTagLength) throws JOSEException { if (ByteUtils.safeBitLength(iv) != IV_BIT_LENGTH) { throw new JOSEException(String.format("IV length of %d bits is required, got %d", IV_BIT_LENGTH, ByteUtils.safeBitLength(iv))); } if (authTagLength != AUTH_TAG_BIT_LENGTH) { throw new JOSEException(String.format("Authentication tag length of %d bits is required, got %d", AUTH_TAG_BIT_LENGTH, authTagLength)); } } /** * Retrieves the actual AES GCM parameters used by the specified * cipher. * * @param cipher The cipher to interrogate. Non-{@code null}. * * @return The AES GCM parameters. Non-{@code null}. * * @throws JOSEException If the parameters cannot be retrieved, are * uninitialized, or are not in the correct form. We want to have the * actual parameters used by the cipher and not rely on the assumption * that they were the same as those we supplied it with. If at runtime * the assumption is incorrect, the ciphertext would not be * decryptable. */ private static GCMParameterSpec actualParamsOf(final Cipher cipher) throws JOSEException { AlgorithmParameters algorithmParameters = cipher.getParameters(); if (algorithmParameters == null) { throw new JOSEException("AES GCM ciphers are expected to make use of algorithm parameters"); } try { // Note: GCMParameterSpec appears in Java 7 return algorithmParameters.getParameterSpec(GCMParameterSpec.class); } catch (InvalidParameterSpecException shouldNotHappen) { throw new JOSEException(shouldNotHappen.getMessage(), shouldNotHappen); } } /** * 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}. * @param provider The JCA provider to use, {@code null} implies the * default. * * @return The decrypted plain text. * * @throws JOSEException If decryption failed. */ public static byte[] decrypt(final SecretKey secretKey, final byte[] iv, final byte[] cipherText, final byte[] authData, final byte[] authTag, final Provider provider) throws JOSEException { // Key alg must be "AES" final SecretKey aesKey = KeyUtils.toAESKey(secretKey); Cipher cipher; try { if (provider != null) { cipher = Cipher.getInstance("AES/GCM/NoPadding", provider); } else { cipher = Cipher.getInstance("AES/GCM/NoPadding"); } GCMParameterSpec gcmSpec = new GCMParameterSpec(AUTH_TAG_BIT_LENGTH, iv); cipher.init(Cipher.DECRYPT_MODE, aesKey, gcmSpec); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) { throw new JOSEException("Couldn't create AES/GCM/NoPadding cipher: " + e.getMessage(), e); } cipher.updateAAD(authData); try { return cipher.doFinal(ByteUtils.concat(cipherText, authTag)); } catch (IllegalBlockSizeException | BadPaddingException e) { throw new JOSEException("AES/GCM/NoPadding decryption failed: " + e.getMessage(), e); } } /** * Prevents public instantiation. */ private AESGCM() { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy