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