001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, 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 java.security.interfaces.RSAPublicKey; 022import java.util.Arrays; 023import java.util.Collections; 024import java.util.HashSet; 025import java.util.Set; 026import javax.crypto.SecretKey; 027 028import com.nimbusds.jose.crypto.impl.*; 029import net.jcip.annotations.ThreadSafe; 030 031import com.nimbusds.jose.EncryptionMethod; 032import com.nimbusds.jose.JOSEException; 033import com.nimbusds.jose.JWEAlgorithm; 034import com.nimbusds.jose.JWECryptoParts; 035import com.nimbusds.jose.JWEEncrypter; 036import com.nimbusds.jose.JWEHeader; 037import com.nimbusds.jose.jwk.RSAKey; 038import com.nimbusds.jose.util.Base64URL; 039 040 041/** 042 * RSA encrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. Expects a 043 * public RSA key. 044 * 045 * <p>Encrypts the plain text with a generated AES key (the Content Encryption 046 * Key) according to the specified JOSE encryption method, then encrypts the 047 * CEK with the public RSA key and returns it alongside the IV, cipher text and 048 * authentication tag. See RFC 7518, sections 049 * <a href="https://tools.ietf.org/html/rfc7518#section-4.2">4.2</a> and 050 * <a href="https://tools.ietf.org/html/rfc7518#section-4.3">4.3</a> for more 051 * information. 052 * 053 * <p>This class is thread-safe. 054 * 055 * <p>Supports the following key management algorithms: 056 * 057 * <ul> 058 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256} 059 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_384} 060 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_512} 061 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} (deprecated) 062 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} (deprecated) 063 * </ul> 064 * 065 * <p>Supports the following content encryption algorithms: 066 * 067 * <ul> 068 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 069 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 070 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 071 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 072 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 073 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 074 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 075 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 076 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 077 * </ul> 078 * 079 * @author David Ortiz 080 * @author Vladimir Dzhuvinov 081 * @author Jun Yu 082 * @version 2021-09-26 083 */ 084@ThreadSafe 085public class RSAEncrypter extends RSACryptoProvider implements JWEEncrypter { 086 087 088 /** 089 * The public RSA key. 090 */ 091 private final RSAPublicKey publicKey; 092 093 094 /** 095 * The externally supplied AES content encryption key (CEK) to use, 096 * {@code null} to generate a CEK for each JWE. 097 */ 098 private final SecretKey contentEncryptionKey; 099 100 101 /** 102 * Creates a new RSA encrypter. 103 * 104 * @param publicKey The public RSA key. Must not be {@code null}. 105 */ 106 public RSAEncrypter(final RSAPublicKey publicKey) { 107 108 this(publicKey, null); 109 } 110 111 112 /** 113 * Creates a new RSA encrypter. 114 * 115 * @param rsaJWK The RSA JSON Web Key (JWK). Must not be {@code null}. 116 * 117 * @throws JOSEException If the RSA JWK extraction failed. 118 */ 119 public RSAEncrypter(final RSAKey rsaJWK) 120 throws JOSEException { 121 122 this(rsaJWK.toRSAPublicKey()); 123 } 124 125 126 /** 127 * Creates a new RSA encrypter with an optionally specified content 128 * encryption key (CEK). 129 * 130 * @param publicKey The public RSA key. Must not be 131 * {@code null}. 132 * @param contentEncryptionKey The content encryption key (CEK) to use. 133 * If specified its algorithm must be "AES" 134 * or "ChaCha20" and its length must match 135 * the expected for the JWE encryption 136 * method ("enc"). If {@code null} a CEK 137 * will be generated for each JWE. 138 */ 139 public RSAEncrypter(final RSAPublicKey publicKey, final SecretKey contentEncryptionKey) { 140 141 if (publicKey == null) { 142 throw new IllegalArgumentException("The public RSA key must not be null"); 143 } 144 this.publicKey = publicKey; 145 146 Set<String> acceptableCEKAlgs = Collections.unmodifiableSet( 147 new HashSet<>(Arrays.asList("AES", "ChaCha20")) 148 ); 149 150 if (contentEncryptionKey != null) { 151 if (contentEncryptionKey.getAlgorithm() == null || ! acceptableCEKAlgs.contains(contentEncryptionKey.getAlgorithm())) { 152 throw new IllegalArgumentException("The algorithm of the content encryption key (CEK) must be AES or ChaCha20"); 153 } else { 154 this.contentEncryptionKey = contentEncryptionKey; 155 } 156 } else { 157 this.contentEncryptionKey = null; 158 } 159 } 160 161 162 /** 163 * Gets the public RSA key. 164 * 165 * @return The public RSA key. 166 */ 167 public RSAPublicKey getPublicKey() { 168 169 return publicKey; 170 } 171 172 173 @Override 174 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 175 throws JOSEException { 176 177 final JWEAlgorithm alg = header.getAlgorithm(); 178 final EncryptionMethod enc = header.getEncryptionMethod(); 179 180 // Generate and encrypt the CEK according to the enc method 181 final SecretKey cek; 182 if (contentEncryptionKey != null) { 183 // Use externally supplied CEK 184 cek = contentEncryptionKey; 185 } else { 186 // Generate and encrypt the CEK according to the enc method 187 cek = ContentCryptoProvider.generateCEK(enc, getJCAContext().getSecureRandom()); 188 } 189 190 final Base64URL encryptedKey; // The second JWE part 191 192 if (alg.equals(JWEAlgorithm.RSA1_5)) { 193 encryptedKey = Base64URL.encode(RSA1_5.encryptCEK(publicKey, cek, getJCAContext().getKeyEncryptionProvider())); 194 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 195 encryptedKey = Base64URL.encode(RSA_OAEP.encryptCEK(publicKey, cek, getJCAContext().getKeyEncryptionProvider())); 196 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) { 197 encryptedKey = Base64URL.encode(RSA_OAEP_SHA2.encryptCEK(publicKey, cek, 256, getJCAContext().getKeyEncryptionProvider())); 198 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_384)) { 199 encryptedKey = Base64URL.encode(RSA_OAEP_SHA2.encryptCEK(publicKey, cek, 384, getJCAContext().getKeyEncryptionProvider())); 200 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_512)) { 201 encryptedKey = Base64URL.encode(RSA_OAEP_SHA2.encryptCEK(publicKey, cek, 512, getJCAContext().getKeyEncryptionProvider())); 202 } else { 203 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 204 } 205 206 return ContentCryptoProvider.encrypt(header, clearText, cek, encryptedKey, getJCAContext()); 207 } 208}