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.PrivateKey;
022import java.util.Set;
023import javax.crypto.SecretKey;
024
025import com.nimbusds.jose.*;
026import com.nimbusds.jose.jwk.RSAKey;
027import com.nimbusds.jose.util.Base64URL;
028import net.jcip.annotations.ThreadSafe;
029
030
031/**
032 * RSA decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. Expects a
033 * private RSA key.
034 *
035 * <p>Decrypts the encrypted Content Encryption Key (CEK) with the private RSA
036 * key, and then uses the CEK along with the IV and authentication tag to
037 * decrypt the cipher text. See RFC 7518, sections
038 * <a href="https://tools.ietf.org/html/rfc7518#section-4.2">4.2</a> and
039 * <a href="https://tools.ietf.org/html/rfc7518#section-4.3">4.3</a> for more
040 * information.
041 *
042 * <p>This class is thread-safe.
043 *
044 * <p>Supports the following key management algorithms:
045 *
046 * <ul>
047 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256}
048 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} (deprecated)
049 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} (deprecated)
050 * </ul>
051 *
052 * <p>Supports the following content encryption algorithms:
053 *
054 * <ul>
055 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
056 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
057 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
058 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
059 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
060 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
061 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
062 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
063 * </ul>
064 * 
065 * @author David Ortiz
066 * @author Vladimir Dzhuvinov
067 * @author Dimitar A. Stoikov
068 * @version 2016-12-01
069 */
070@ThreadSafe
071public class RSADecrypter extends RSACryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware {
072
073
074        /**
075         * The critical header policy.
076         */
077        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
078
079
080        /**
081         * The private RSA key.
082         */
083        private final PrivateKey privateKey;
084        
085        
086        /**
087         * Stores a CEK decryption exception is one was encountered during the
088         * last {@link #decrypt} run.
089         */
090        private Exception cekDecryptionException;
091
092
093        /**
094         * Creates a new RSA decrypter. This constructor can also accept a
095         * private RSA key located in a PKCS#11 store that doesn't expose the
096         * private key parameters (such as a smart card or HSM).
097         *
098         * @param privateKey The private RSA key. Must not be {@code null}.
099         */
100        public RSADecrypter(final PrivateKey privateKey) {
101
102                this(privateKey, null);
103        }
104
105
106        /**
107         * Creates a new RSA decrypter.
108         *
109         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain a private
110         *               part. Must not be {@code null}.
111         *
112         * @throws JOSEException If the RSA JWK doesn't contain a private part
113         *                       or its extraction failed.
114         */
115        public RSADecrypter(final RSAKey rsaJWK)
116                throws JOSEException {
117
118                if (! rsaJWK.isPrivate()) {
119                        throw new JOSEException("The RSA JWK doesn't contain a private part");
120                }
121
122                privateKey = rsaJWK.toPrivateKey();
123        }
124
125
126        /**
127         * Creates a new RSA decrypter. This constructor can also accept a
128         * private RSA key located in a key store that doesn't expose the
129         * private key parameters (such as a smart card or HSM).
130         *
131         * @param privateKey     The private RSA key. Its algorithm must be
132         *                       "RSA". Must not be {@code null}.
133         * @param defCritHeaders The names of the critical header parameters
134         *                       that are deferred to the application for
135         *                       processing, empty set or {@code null} if none.
136         */
137        public RSADecrypter(final PrivateKey privateKey,
138                            final Set<String> defCritHeaders) {
139
140                if (privateKey == null) {
141                        throw new IllegalArgumentException("The private RSA key must not be null");
142                }
143
144                if (! privateKey.getAlgorithm().equalsIgnoreCase("RSA")) {
145                        throw new IllegalArgumentException("The private key algorithm must be RSA");
146                }
147
148                this.privateKey = privateKey;
149
150                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
151        }
152
153
154        /**
155         * Gets the private RSA key.
156         *
157         * @return The private RSA key. Casting to
158         *         {@link java.security.interfaces.RSAPrivateKey} may not be
159         *         possible if the key is located in a PKCS#11 store that
160         *         doesn't expose the private key parameters.
161         */
162        public PrivateKey getPrivateKey() {
163
164                return privateKey;
165        }
166
167
168        @Override
169        public Set<String> getProcessedCriticalHeaderParams() {
170
171                return critPolicy.getProcessedCriticalHeaderParams();
172        }
173
174
175        @Override
176        public Set<String> getDeferredCriticalHeaderParams() {
177
178                return critPolicy.getProcessedCriticalHeaderParams();
179        }
180
181
182        @Override
183        public byte[] decrypt(final JWEHeader header,
184                              final Base64URL encryptedKey,
185                              final Base64URL iv,
186                              final Base64URL cipherText,
187                              final Base64URL authTag) 
188                throws JOSEException {
189
190                // Validate required JWE parts
191                if (encryptedKey == null) {
192                        throw new JOSEException("Missing JWE encrypted key");
193                }       
194
195                if (iv == null) {
196                        throw new JOSEException("Missing JWE initialization vector (IV)");
197                }
198
199                if (authTag == null) {
200                        throw new JOSEException("Missing JWE authentication tag");
201                }
202
203                critPolicy.ensureHeaderPasses(header);
204                
205
206                // Derive the content encryption key
207                JWEAlgorithm alg = header.getAlgorithm();
208
209                SecretKey cek;
210
211                if (alg.equals(JWEAlgorithm.RSA1_5)) {
212
213                        int keyLength = header.getEncryptionMethod().cekBitLength();
214
215                        // Protect against MMA attack by generating random CEK to be used on decryption failure,
216                        // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html
217                        final SecretKey randomCEK = ContentCryptoProvider.generateCEK(header.getEncryptionMethod(), getJCAContext().getSecureRandom());
218
219                        try {
220                                cek = RSA1_5.decryptCEK(privateKey, encryptedKey.decode(), keyLength, getJCAContext().getKeyEncryptionProvider());
221
222                                if (cek == null) {
223                                        // CEK length mismatch, signalled by null instead of
224                                        // exception to prevent MMA attack
225                                        cek = randomCEK;
226                                }
227
228                        } catch (Exception e) {
229                                // continue
230                                cekDecryptionException = e;
231                                cek = randomCEK;
232                        }
233                        
234                        cekDecryptionException = null;
235                
236                } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
237
238                        cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider());
239
240                } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) {
241                        
242                        cek = RSA_OAEP_256.decryptCEK(privateKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider());
243                        
244                } else {
245                
246                        throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS));
247                }
248
249                return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext());
250        }
251        
252        
253        /**
254         * Returns the Content Encryption Key (CEK) decryption exception if one
255         * was encountered during the last {@link #decrypt} run. Intended for
256         * logging and debugging purposes.
257         *
258         * @return The recorded exception, {@code null} if none.
259         */
260        public Exception getCEKDecryptionException() {
261                
262                return cekDecryptionException;
263        }
264}
265