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}