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