JSON Web Encryption (JWE) with a preset Content Encryption Key (CEK)

Version 5.13 of the Nimbus JOSE+JWT library updated the RSAEncrypter to enable passing of an externally generated Content Encryption Key (CEK). The default encrypter behaviour is to internally generate a new CEK for each JWE secured object that is about to be encrypted.

The length of the passed CEK must match the expected bit length for the encryption method that is going to be used. For example, the A128CBC-HS256 requires a 256 bit CEK. The CEK bit length can be found out programmatically like this:

EncryptionMethod.A128CBC_HS256.cekBitLength();

Example use of a preset AES CEK for a JWE with RSA-OAEP-256 and A128CBC-HS256:

import java.security.*;
import java.security.interfaces.*;
import javax.crypto.*;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;

// The JWE alg and enc
JWEAlgorithm alg = JWEAlgorithm.RSA_OAEP_256;
EncryptionMethod enc = EncryptionMethod.A128CBC_HS256;

// Generate an RSA key pair
KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA");
rsaGen.initialize(2048);
KeyPair rsaKeyPair = rsaGen.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey)rsaKeyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)rsaKeyPair.getPrivate();

// Generate the Content Encryption Key (CEK)
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(enc.cekBitLength());
SecretKey cek = keyGenerator.generateKey();

// Encrypt the JWE with the RSA public key + specified AES CEK
JWEObject jwe = new JWEObject(
    new JWEHeader(alg, enc),
    new Payload("Hello, world!"));
jwe.encrypt(new RSAEncrypter(rsaPublicKey, cek));
String jweString = jwe.serialize();

// Decrypt the JWE with the RSA private key
jwe = JWEObject.parse(jweString);
jwe.decrypt(new RSADecrypter(rsaPrivateKey));
assertEquals("Hello, world!", jwe.getPayload().toString());

// Decrypt JWE with CEK directly, with the DirectDecrypter in promiscuous mode
jwe = JWEObject.parse(jweString);
jwe.decrypt(new DirectDecrypter(cek, true));
assertEquals("Hello, world!", jwe.getPayload().toString());

If you have a use case which requires a preset CEK for some other encrypter, for example the ECDHEncrypter, feel free to submit a pull request.