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

com.nimbusds.jose.crypto.impl.AESCBC 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.48
Show newest version
/*
 * nimbus-jose-jwt
 *
 * Copyright 2012-2016, 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 java.nio.ByteBuffer;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import net.jcip.annotations.ThreadSafe;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWEHeader;
import com.nimbusds.jose.crypto.utils.ConstantTimeUtils;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.util.ByteUtils;
import com.nimbusds.jose.util.StandardCharset;


/**
 * AES/CBC/PKCS5Padding and AES/CBC/PKCS5Padding/HMAC-SHA2 encryption and 
 * decryption methods. This class is thread-safe.
 *
 * 

Also supports the deprecated AES/CBC/HMAC encryption using a custom * concat KDF (JOSE draft suite 08). * *

See RFC 7518 (JWA), section 5.2. * * @author Vladimir Dzhuvinov * @author Axel Nennker * @version 2022-01-24 */ @ThreadSafe public class AESCBC { /** * The standard Initialisation Vector (IV) length (128 bits). */ public static final int IV_BIT_LENGTH = 128; /** * Generates a random 128 bit (16 byte) Initialisation Vector(IV) for * use in AES-CBC encryption. * * @param randomGen The secure random generator to use. Must be * correctly initialised and not {@code null}. * * @return The random 128 bit IV, as 16 byte array. */ public static byte[] generateIV(final SecureRandom randomGen) { byte[] bytes = new byte[ByteUtils.byteLength(IV_BIT_LENGTH)]; randomGen.nextBytes(bytes); return bytes; } /** * Creates a new AES/CBC/PKCS5Padding 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 provider The JCA provider, {@code null} to use the * default. * * @return The AES/CBC/PKCS5Padding cipher. */ private static Cipher createAESCBCCipher(final SecretKey secretKey, final boolean forEncryption, final byte[] iv, final Provider provider) throws JOSEException { Cipher cipher; try { cipher = CipherHelper.getInstance("AES/CBC/PKCS5Padding", provider); SecretKeySpec keyspec = new SecretKeySpec(secretKey.getEncoded(), "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); if (forEncryption) { cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivSpec); } else { cipher.init(Cipher.DECRYPT_MODE, keyspec, ivSpec); } } catch (Exception e) { throw new JOSEException(e.getMessage(), e); } return cipher; } /** * Encrypts the specified plain text using AES/CBC/PKCS5Padding. * * @param secretKey The AES key. Must not be {@code null}. * @param iv The initialisation vector (IV). Must not be * {@code null}. * @param plainText The plain text. Must not be {@code null}. * @param provider The JCA provider, {@code null} to use the default. * * @return The cipher text. * * @throws JOSEException If encryption failed. */ public static byte[] encrypt(final SecretKey secretKey, final byte[] iv, final byte[] plainText, final Provider provider) throws JOSEException { Cipher cipher = createAESCBCCipher(secretKey, true, iv, provider); try { return cipher.doFinal(plainText); } catch (Exception e) { throw new JOSEException(e.getMessage(), e); } } /** * Encrypts the specified plain text using AES/CBC/PKCS5Padding/ * HMAC-SHA2. * *

See RFC 7518 (JWA), section 5.2.2.1 * *

See draft-mcgrew-aead-aes-cbc-hmac-sha2-01 * * @param secretKey The secret key. Must be 256 or 512 bits long. * Must not be {@code null}. * @param iv The initialisation vector (IV). Must not be * {@code null}. * @param plainText The plain text. Must not be {@code null}. * @param aad The additional authenticated data. Must not be * {@code null}. * @param ceProvider The JCA provider for the content encryption, or * {@code null} to use the default one. * @param macProvider The JCA provider for the MAC computation, or * {@code null} to use the default one. * * @return The authenticated cipher text. * * @throws JOSEException If encryption failed. */ public static AuthenticatedCipherText encryptAuthenticated(final SecretKey secretKey, final byte[] iv, final byte[] plainText, final byte[] aad, final Provider ceProvider, final Provider macProvider) throws JOSEException { // Extract MAC + AES/CBC keys from input secret key CompositeKey compositeKey = new CompositeKey(secretKey); // Encrypt plain text byte[] cipherText = encrypt(compositeKey.getAESKey(), iv, plainText, ceProvider); // AAD length to 8 byte array byte[] al = AAD.computeLength(aad); // Do MAC int hmacInputLength = aad.length + iv.length + cipherText.length + al.length; byte[] hmacInput = ByteBuffer.allocate(hmacInputLength).put(aad).put(iv).put(cipherText).put(al).array(); byte[] hmac = HMAC.compute(compositeKey.getMACKey(), hmacInput, macProvider); byte[] authTag = Arrays.copyOf(hmac, compositeKey.getTruncatedMACByteLength()); return new AuthenticatedCipherText(cipherText, authTag); } /** * Encrypts the specified plain text using the deprecated concat KDF * from JOSE draft suite 09. * * @param header The JWE header. Must not be {@code null}. * @param secretKey The secret key. Must be 256 or 512 bits long. * Must not be {@code null}. * @param encryptedKey The encrypted key. Must not be {@code null}. * @param iv The initialisation vector (IV). Must not be * {@code null}. * @param plainText The plain text. Must not be {@code null}. * @param ceProvider The JCA provider for the content encryption, or * {@code null} to use the default one. * @param macProvider The JCA provider for the MAC computation, or * {@code null} to use the default one. * * @return The authenticated cipher text. * * @throws JOSEException If encryption failed. */ public static AuthenticatedCipherText encryptWithConcatKDF(final JWEHeader header, final SecretKey secretKey, final Base64URL encryptedKey, final byte[] iv, final byte[] plainText, final Provider ceProvider, final Provider macProvider) throws JOSEException { byte[] epu = null; if (header.getCustomParam("epu") instanceof String) { epu = new Base64URL((String)header.getCustomParam("epu")).decode(); } byte[] epv = null; if (header.getCustomParam("epv") instanceof String) { epv = new Base64URL((String)header.getCustomParam("epv")).decode(); } // Generate alternative CEK using concat-KDF SecretKey altCEK = LegacyConcatKDF.generateCEK(secretKey, header.getEncryptionMethod(), epu, epv); byte[] cipherText = AESCBC.encrypt(altCEK, iv, plainText, ceProvider); // Generate content integrity key for HMAC SecretKey cik = LegacyConcatKDF.generateCIK(secretKey, header.getEncryptionMethod(), epu, epv); String macInput = header.toBase64URL() + "." + encryptedKey + "." + Base64URL.encode(iv) + "." + Base64URL.encode(cipherText); byte[] mac = HMAC.compute(cik, macInput.getBytes(StandardCharset.UTF_8), macProvider); return new AuthenticatedCipherText(cipherText, mac); } /** * Decrypts the specified cipher text using AES/CBC/PKCS5Padding. * * @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 provider The JCA provider, {@code null} to use 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 Provider provider) throws JOSEException { Cipher cipher = createAESCBCCipher(secretKey, false, iv, provider); try { return cipher.doFinal(cipherText); } catch (Exception e) { throw new JOSEException(e.getMessage(), e); } } /** * Decrypts the specified cipher text using AES/CBC/PKCS5Padding/ * HMAC-SHA2. * *

See RFC 7518 (JWA), section 5.2.2.2 * *

See draft-mcgrew-aead-aes-cbc-hmac-sha2-01 * * @param secretKey The secret key. Must be 256 or 512 bits long. * 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 aad The additional authenticated data. Must not be * {@code null}. * @param authTag The authentication tag. Must not be {@code null}. * @param ceProvider The JCA provider for the content encryption, or * {@code null} to use the default one. * @param macProvider The JCA provider for the MAC computation, or * {@code null} to use the default one. * * @return The decrypted plain text. * * @throws JOSEException If decryption failed. */ public static byte[] decryptAuthenticated(final SecretKey secretKey, final byte[] iv, final byte[] cipherText, final byte[] aad, final byte[] authTag, final Provider ceProvider, final Provider macProvider) throws JOSEException { // Extract MAC + AES/CBC keys from input secret key CompositeKey compositeKey = new CompositeKey(secretKey); // AAD length to 8 byte array byte[] al = AAD.computeLength(aad); // Check MAC int hmacInputLength = aad.length + iv.length + cipherText.length + al.length; byte[] hmacInput = ByteBuffer.allocate(hmacInputLength). put(aad). put(iv). put(cipherText). put(al). array(); byte[] hmac = HMAC.compute(compositeKey.getMACKey(), hmacInput, macProvider); byte[] expectedAuthTag = Arrays.copyOf(hmac, compositeKey.getTruncatedMACByteLength()); if (! ConstantTimeUtils.areEqual(expectedAuthTag, authTag)) { throw new JOSEException("MAC check failed"); } return decrypt(compositeKey.getAESKey(), iv, cipherText, ceProvider); } /** * Decrypts the specified cipher text using the deprecated concat KDF * from JOSE draft suite 09. * * @param header The JWE header. Must not be {@code null}. * @param secretKey The secret key. Must be 256 or 512 bits long. * Must not be {@code null}. * @param encryptedKey The encrypted 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 authTag The authentication tag. Must not be {@code null}. * @param ceProvider The JCA provider for the content encryption, or * {@code null} to use the default one. * @param macProvider The JCA provider for the MAC computation, or * {@code null} to use the default one. * * @return The decrypted plain text. * * @throws JOSEException If decryption failed. */ public static byte[] decryptWithConcatKDF(final JWEHeader header, final SecretKey secretKey, final Base64URL encryptedKey, final Base64URL iv, final Base64URL cipherText, final Base64URL authTag, final Provider ceProvider, final Provider macProvider) throws JOSEException { byte[] epu = null; if (header.getCustomParam("epu") instanceof String) { epu = new Base64URL((String)header.getCustomParam("epu")).decode(); } byte[] epv = null; if (header.getCustomParam("epv") instanceof String) { epv = new Base64URL((String)header.getCustomParam("epv")).decode(); } SecretKey cik = LegacyConcatKDF.generateCIK(secretKey, header.getEncryptionMethod(), epu, epv); String macInput = header.toBase64URL().toString() + "." + encryptedKey.toString() + "." + iv.toString() + "." + cipherText.toString(); byte[] mac = HMAC.compute(cik, macInput.getBytes(StandardCharset.UTF_8), macProvider); if (! ConstantTimeUtils.areEqual(authTag.decode(), mac)) { throw new JOSEException("MAC check failed"); } SecretKey cekAlt = LegacyConcatKDF.generateCEK(secretKey, header.getEncryptionMethod(), epu, epv); return AESCBC.decrypt(cekAlt, iv.decode(), cipherText.decode(), ceProvider); } /** * Prevents public instantiation. */ private AESCBC() { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy