001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2025, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.crypto; 019 020 021import com.nimbusds.jose.*; 022import com.nimbusds.jose.crypto.impl.*; 023import com.nimbusds.jose.crypto.opts.CipherMode; 024import com.nimbusds.jose.jwk.RSAKey; 025import com.nimbusds.jose.util.Base64URL; 026import net.jcip.annotations.ThreadSafe; 027 028import javax.crypto.SecretKey; 029import java.security.interfaces.RSAPublicKey; 030import java.util.Collections; 031import java.util.Objects; 032import java.util.Set; 033 034 035/** 036 * RSA encrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. Expects a 037 * public RSA key. 038 * 039 * <p>Encrypts the plain text with a generated AES key (the Content Encryption 040 * Key) according to the specified JOSE encryption method, then encrypts the 041 * CEK with the public RSA key and returns it alongside the IV, cipher text and 042 * authentication tag. See RFC 7518, sections 043 * <a href="https://tools.ietf.org/html/rfc7518#section-4.2">4.2</a> and 044 * <a href="https://tools.ietf.org/html/rfc7518#section-4.3">4.3</a> for more 045 * information. 046 * 047 * <p>This class is thread-safe. 048 * 049 * <p>Supports the following key management algorithms: 050 * 051 * <ul> 052 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256} 053 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_384} 054 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_512} 055 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} (deprecated) 056 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} (deprecated) 057 * </ul> 058 * 059 * <p>Supports the following content encryption algorithms: 060 * 061 * <ul> 062 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 063 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 064 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 065 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 066 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 067 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 068 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 069 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 070 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 071 * </ul> 072 * 073 * @author David Ortiz 074 * @author Vladimir Dzhuvinov 075 * @author Jun Yu 076 * @author Egor Puzanov 077 * @version 2025-07-19 078 */ 079@ThreadSafe 080public class RSAEncrypter extends RSACryptoProvider implements JWEEncrypter { 081 082 083 /** 084 * The public RSA key. 085 */ 086 private final RSAPublicKey publicKey; 087 088 089 /** 090 * The configured options, empty set if none. 091 */ 092 private final Set<JWEEncrypterOption> opts; 093 094 095 /** 096 * Creates a new RSA encrypter. 097 * 098 * @param publicKey The public RSA key. Must not be {@code null}. 099 */ 100 public RSAEncrypter(final RSAPublicKey publicKey) { 101 102 this(publicKey, null); 103 } 104 105 106 /** 107 * Creates a new RSA encrypter. 108 * 109 * @param rsaJWK The RSA JSON Web Key (JWK). Must not be {@code null}. 110 * 111 * @throws JOSEException If the RSA JWK extraction failed. 112 */ 113 public RSAEncrypter(final RSAKey rsaJWK) 114 throws JOSEException { 115 116 this(rsaJWK.toRSAPublicKey()); 117 } 118 119 120 /** 121 * Creates a new RSA encrypter with an optionally specified content 122 * encryption key (CEK). 123 * 124 * @param publicKey The public RSA key. Must not be 125 * {@code null}. 126 * @param contentEncryptionKey The content encryption key (CEK) to use. 127 * If specified its algorithm must be "AES" 128 * or "ChaCha20" and its length must match 129 * the expected for the JWE encryption 130 * method ("enc"). If {@code null} a CEK 131 * will be generated for each JWE. 132 */ 133 public RSAEncrypter(final RSAPublicKey publicKey, final SecretKey contentEncryptionKey) { 134 135 this(publicKey, contentEncryptionKey, Collections.<JWEEncrypterOption>emptySet()); 136 } 137 138 139 /** 140 * Creates a new RSA encrypter with an optionally specified content 141 * encryption key (CEK). 142 * 143 * @param publicKey The public RSA key. Must not be 144 * {@code null}. 145 * @param contentEncryptionKey The content encryption key (CEK) to use. 146 * If specified its algorithm must be "AES" 147 * or "ChaCha20" and its length must match 148 * the expected for the JWE encryption 149 * method ("enc"). If {@code null} a CEK 150 * will be generated for each JWE. 151 * @param opts The encryption options, empty or 152 * {@code null} if none. 153 */ 154 public RSAEncrypter(final RSAPublicKey publicKey, 155 final SecretKey contentEncryptionKey, 156 final Set<JWEEncrypterOption> opts) { 157 158 super(contentEncryptionKey); 159 this.publicKey = Objects.requireNonNull(publicKey); 160 this.opts = opts != null ? opts : Collections.<JWEEncrypterOption>emptySet(); 161 } 162 163 164 /** 165 * Gets the public RSA key. 166 * 167 * @return The public RSA key. 168 */ 169 public RSAPublicKey getPublicKey() { 170 171 return publicKey; 172 } 173 174 175 /** 176 * Encrypts the specified clear text of a {@link JWEObject JWE object}. 177 * 178 * @param header The JSON Web Encryption (JWE) header. Must specify 179 * a supported JWE algorithm and method. Must not be 180 * {@code null}. 181 * @param clearText The clear text to encrypt. Must not be {@code null}. 182 * 183 * @return The resulting JWE crypto parts. 184 * 185 * @throws JOSEException If the JWE algorithm or method is not 186 * supported or if encryption failed for some 187 * other internal reason. 188 */ 189 @Deprecated 190 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 191 throws JOSEException { 192 193 return encrypt(header, clearText, AAD.compute(header)); 194 } 195 196 197 private CipherMode resolveCipherModeForOAEP() { 198 199 if (opts.contains(CipherMode.ENCRYPT_DECRYPT)) { 200 return CipherMode.ENCRYPT_DECRYPT; 201 } else { 202 // The default cipher mode for RSA OAEP 203 return CipherMode.WRAP_UNWRAP; 204 } 205 // The contains ENCRYPT_DECRYPT / WRAP_UNWRAP is not checked 206 } 207 208 209 @Override 210 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText, final byte[] aad) 211 throws JOSEException { 212 213 final JWEAlgorithm alg = JWEHeaderValidation.getAlgorithmAndEnsureNotNull(header); 214 final EncryptionMethod enc = header.getEncryptionMethod(); 215 final SecretKey cek = getCEK(enc); // Generate and encrypt the CEK according to the enc method 216 217 final Base64URL encryptedKey; // The second JWE part 218 219 if (alg.equals(JWEAlgorithm.RSA1_5)) { 220 encryptedKey = Base64URL.encode(RSA1_5.encryptCEK(publicKey, cek, getJCAContext().getKeyEncryptionProvider())); 221 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 222 encryptedKey = Base64URL.encode(RSA_OAEP.encryptCEK(publicKey, cek, resolveCipherModeForOAEP(), getJCAContext().getKeyEncryptionProvider())); 223 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) { 224 encryptedKey = Base64URL.encode(RSA_OAEP_SHA2.encryptCEK(publicKey, cek, 256, resolveCipherModeForOAEP(), getJCAContext().getKeyEncryptionProvider())); 225 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_384)) { 226 encryptedKey = Base64URL.encode(RSA_OAEP_SHA2.encryptCEK(publicKey, cek, 384, resolveCipherModeForOAEP(), getJCAContext().getKeyEncryptionProvider())); 227 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_512)) { 228 encryptedKey = Base64URL.encode(RSA_OAEP_SHA2.encryptCEK(publicKey, cek, 512, resolveCipherModeForOAEP(), getJCAContext().getKeyEncryptionProvider())); 229 } else { 230 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 231 } 232 233 return ContentCryptoProvider.encrypt(header, clearText, aad, cek, encryptedKey, getJCAContext()); 234 } 235}