001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2019, 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.jwt.proc;
019
020
021import java.security.Key;
022import java.text.ParseException;
023import java.util.List;
024import java.util.ListIterator;
025
026import com.nimbusds.jose.*;
027import com.nimbusds.jose.crypto.factories.DefaultJWEDecrypterFactory;
028import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory;
029import com.nimbusds.jose.proc.*;
030import com.nimbusds.jwt.*;
031
032
033/**
034 * Default processor of {@link com.nimbusds.jwt.PlainJWT unsecured} (plain),
035 * {@link com.nimbusds.jwt.SignedJWT signed} and
036 * {@link com.nimbusds.jwt.EncryptedJWT encrypted} JSON Web Tokens (JWTs).
037 *
038 * <p>Must be configured with the following:
039 *
040 * <ol>
041 *     <li>To process signed JWTs: A JWS key selector using the
042 *     {@link JWSKeySelector header} or the
043 *     {@link JWTClaimsSetAwareJWSKeySelector header and claims set} to
044 *     determine the key candidate(s) for the signature verification. The key
045 *     selection procedure is application-specific and may involve key ID
046 *     lookup, a certificate check and / or some
047 *     {@link SecurityContext context}.</li>
048 *
049 *     <li>To process encrypted JWTs: A JWE key selector using the
050 *     {@link JWEKeySelector header} to determine the key candidate(s) for
051 *     decryption. The key selection procedure is application-specific and may
052 *     involve key ID lookup, a certificate check and / or some
053 *     {@link SecurityContext context}.</li>
054 * </ol>
055 *
056 * <p>An optional context parameter is available to facilitate passing of
057 * additional data between the caller and the underlying selector of key
058 * candidates (in both directions).
059 *
060 * <p>See sections 6 of RFC 7515 (JWS) and RFC 7516 (JWE) for guidelines on key
061 * selection.
062 *
063 * <p>This processor comes with the default {@link DefaultJWSVerifierFactory
064 * JWS verifier factory} and the default {@link DefaultJWEDecrypterFactory
065 * JWE decrypter factory}; they can construct verifiers / decrypters for all
066 * standard JOSE algorithms implemented by the library.
067 *
068 * <p>Note that for security reasons this processor is hardwired to reject
069 * unsecured (plain) JWTs. Override the {@link #process(PlainJWT, SecurityContext)}
070 * if you need to handle plain JWTs as well.
071 *
072 * <p>A {@link DefaultJWTClaimsVerifier default JWT claims verifier} is
073 * provided, to perform a minimal check of the claims after a successful JWS
074 * verification / JWE decryption. It checks the token expiration (exp) and
075 * not-before (nbf) timestamps if these are present. The default JWT claims
076 * verifier may be extended to perform additional checks, such as issuer and
077 * subject acceptance.
078 *
079 * <p>To process generic JOSE objects (with arbitrary payloads) use the
080 * {@link com.nimbusds.jose.proc.DefaultJOSEProcessor} class.
081 *
082 * @author Vladimir Dzhuvinov
083 * @version 2019-07-18
084 */
085public class DefaultJWTProcessor<C extends SecurityContext>
086        implements ConfigurableJWTProcessor<C> {
087
088        // Cache exceptions
089        private static final BadJOSEException PLAIN_JWT_REJECTED_EXCEPTION =
090                new BadJOSEException("Unsecured (plain) JWTs are rejected, extend class to handle");
091        private static final BadJOSEException NO_JWS_KEY_SELECTOR_EXCEPTION =
092                new BadJOSEException("Signed JWT rejected: No JWS key selector is configured");
093        private static final BadJOSEException NO_JWE_KEY_SELECTOR_EXCEPTION =
094                new BadJOSEException("Encrypted JWT rejected: No JWE key selector is configured");
095        private static final JOSEException NO_JWS_VERIFIER_FACTORY_EXCEPTION =
096                new JOSEException("No JWS verifier is configured");
097        private static final JOSEException NO_JWE_DECRYPTER_FACTORY_EXCEPTION =
098                new JOSEException("No JWE decrypter is configured");
099        private static final BadJOSEException NO_JWS_KEY_CANDIDATES_EXCEPTION =
100                new BadJOSEException("Signed JWT rejected: Another algorithm expected, or no matching key(s) found");
101        private static final BadJOSEException NO_JWE_KEY_CANDIDATES_EXCEPTION =
102                new BadJOSEException("Encrypted JWT rejected: Another algorithm expected, or no matching key(s) found");
103        private static final BadJOSEException INVALID_SIGNATURE =
104                new BadJWSException("Signed JWT rejected: Invalid signature");
105        private static final BadJWTException INVALID_NESTED_JWT_EXCEPTION =
106                new BadJWTException("The payload is not a nested signed JWT");
107        private static final BadJOSEException NO_MATCHING_VERIFIERS_EXCEPTION =
108                new BadJOSEException("JWS object rejected: No matching verifier(s) found");
109        private static final BadJOSEException NO_MATCHING_DECRYPTERS_EXCEPTION =
110                new BadJOSEException("Encrypted JWT rejected: No matching decrypter(s) found");
111
112        /**
113         * The JWS key selector.
114         */
115        private JWSKeySelector<C> jwsKeySelector;
116        
117        
118        /**
119         * The JWT claims aware JWS key selector, alternative to
120         * {@link #jwsKeySelector}.
121         */
122        private JWTClaimsSetAwareJWSKeySelector<C> claimsSetAwareJWSKeySelector;
123
124
125        /**
126         * The JWE key selector.
127         */
128        private JWEKeySelector<C> jweKeySelector;
129
130
131        /**
132         * The JWS verifier factory.
133         */
134        private JWSVerifierFactory jwsVerifierFactory = new DefaultJWSVerifierFactory();
135
136
137        /**
138         * The JWE decrypter factory.
139         */
140        private JWEDecrypterFactory jweDecrypterFactory = new DefaultJWEDecrypterFactory();
141
142
143        /**
144         * The claims verifier.
145         */
146        private JWTClaimsSetVerifier<C> claimsVerifier = new DefaultJWTClaimsVerifier<>();
147        
148        
149        /**
150         * The deprecated claims verifier.
151         */
152        private JWTClaimsVerifier deprecatedClaimsVerifier = null;
153
154
155        @Override
156        public JWSKeySelector<C> getJWSKeySelector() {
157
158                return jwsKeySelector;
159        }
160
161
162        @Override
163        public void setJWSKeySelector(final JWSKeySelector<C> jwsKeySelector) {
164
165                this.jwsKeySelector = jwsKeySelector;
166        }
167        
168        
169        @Override
170        public JWTClaimsSetAwareJWSKeySelector<C> getJWTClaimsSetAwareJWSKeySelector() {
171                
172                return claimsSetAwareJWSKeySelector;
173        }
174        
175        
176        @Override
177        public void setJWTClaimsSetAwareJWSKeySelector(final JWTClaimsSetAwareJWSKeySelector<C> jwsKeySelector) {
178        
179                this.claimsSetAwareJWSKeySelector = jwsKeySelector;
180        }
181        
182        
183        @Override
184        public JWEKeySelector<C> getJWEKeySelector() {
185
186                return jweKeySelector;
187        }
188
189
190        @Override
191        public void setJWEKeySelector(final JWEKeySelector<C> jweKeySelector) {
192
193                this.jweKeySelector = jweKeySelector;
194        }
195
196
197        @Override
198        public JWSVerifierFactory getJWSVerifierFactory() {
199
200                return jwsVerifierFactory;
201        }
202
203
204        @Override
205        public void setJWSVerifierFactory(final JWSVerifierFactory factory) {
206
207                jwsVerifierFactory = factory;
208        }
209
210
211        @Override
212        public JWEDecrypterFactory getJWEDecrypterFactory() {
213
214                return jweDecrypterFactory;
215        }
216
217
218        @Override
219        public void setJWEDecrypterFactory(final JWEDecrypterFactory factory) {
220
221                jweDecrypterFactory = factory;
222        }
223        
224        
225        @Override
226        public JWTClaimsSetVerifier<C> getJWTClaimsSetVerifier() {
227                
228                return claimsVerifier;
229        }
230        
231        
232        @Override
233        public void setJWTClaimsSetVerifier(final JWTClaimsSetVerifier<C> claimsVerifier) {
234                
235                this.claimsVerifier = claimsVerifier;
236                this.deprecatedClaimsVerifier = null; // clear other verifier
237        }
238        
239        
240        @Override
241        @Deprecated
242        public JWTClaimsVerifier getJWTClaimsVerifier() {
243
244                return deprecatedClaimsVerifier;
245        }
246
247
248        @Override
249        @Deprecated
250        public void setJWTClaimsVerifier(final JWTClaimsVerifier claimsVerifier) {
251
252                this.claimsVerifier = null; // clear official verifier
253                this.deprecatedClaimsVerifier = claimsVerifier;
254        }
255        
256        
257        private JWTClaimsSet extractJWTClaimsSet(final JWT jwt)
258                throws BadJWTException {
259                
260                try {
261                        return jwt.getJWTClaimsSet();
262                } catch (ParseException e) {
263                        // Payload not a JSON object
264                        throw new BadJWTException(e.getMessage(), e);
265                }
266        }
267
268
269        private JWTClaimsSet verifyClaims(final JWTClaimsSet claimsSet, final C context)
270                throws BadJWTException {
271                
272                if (getJWTClaimsSetVerifier() != null) {
273                        getJWTClaimsSetVerifier().verify(claimsSet, context);
274                } else if (getJWTClaimsVerifier() != null) {
275                        // Fall back to deprecated claims verifier
276                        getJWTClaimsVerifier().verify(claimsSet);
277                }
278                return claimsSet;
279        }
280        
281        
282        private List<? extends Key> selectKeys(final JWSHeader header, final JWTClaimsSet claimsSet, final C context)
283                throws KeySourceException, BadJOSEException {
284                
285                if (getJWTClaimsSetAwareJWSKeySelector() != null) {
286                        return getJWTClaimsSetAwareJWSKeySelector().selectKeys(header, claimsSet, context);
287                } else if (getJWSKeySelector() != null) {
288                        return getJWSKeySelector().selectJWSKeys(header, context);
289                } else {
290                        throw NO_JWS_KEY_SELECTOR_EXCEPTION;
291                }
292        }
293
294
295        @Override
296        public JWTClaimsSet process(final String jwtString, final C context)
297                throws ParseException, BadJOSEException, JOSEException {
298
299                return process(JWTParser.parse(jwtString), context);
300        }
301
302
303        @Override
304        public JWTClaimsSet process(final JWT jwt, final C context)
305                throws BadJOSEException, JOSEException {
306
307                if (jwt instanceof SignedJWT) {
308                        return process((SignedJWT)jwt, context);
309                }
310
311                if (jwt instanceof EncryptedJWT) {
312                        return process((EncryptedJWT)jwt, context);
313                }
314
315                if (jwt instanceof PlainJWT) {
316                        return process((PlainJWT)jwt, context);
317                }
318
319                // Should never happen
320                throw new JOSEException("Unexpected JWT object type: " + jwt.getClass());
321        }
322
323
324        @Override
325        public JWTClaimsSet process(final PlainJWT plainJWT, final C context)
326                throws BadJOSEException, JOSEException {
327
328                throw PLAIN_JWT_REJECTED_EXCEPTION;
329        }
330
331
332        @Override
333        public JWTClaimsSet process(final SignedJWT signedJWT, final C context)
334                throws BadJOSEException, JOSEException {
335
336                if (getJWSKeySelector() == null && getJWTClaimsSetAwareJWSKeySelector() == null) {
337                        // JWS key selector may have been deliberately omitted
338                        throw NO_JWS_KEY_SELECTOR_EXCEPTION;
339                }
340
341                if (getJWSVerifierFactory() == null) {
342                        throw NO_JWS_VERIFIER_FACTORY_EXCEPTION;
343                }
344                
345                JWTClaimsSet claimsSet = extractJWTClaimsSet(signedJWT);
346
347                List<? extends Key> keyCandidates = selectKeys(signedJWT.getHeader(), claimsSet, context);
348
349                if (keyCandidates == null || keyCandidates.isEmpty()) {
350                        throw NO_JWS_KEY_CANDIDATES_EXCEPTION;
351                }
352
353                ListIterator<? extends Key> it = keyCandidates.listIterator();
354
355                while (it.hasNext()) {
356
357                        JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(signedJWT.getHeader(), it.next());
358
359                        if (verifier == null) {
360                                continue;
361                        }
362
363                        final boolean validSignature = signedJWT.verify(verifier);
364
365                        if (validSignature) {
366                                return verifyClaims(claimsSet, context);
367                        }
368
369                        if (! it.hasNext()) {
370                                // No more keys to try out
371                                throw INVALID_SIGNATURE;
372                        }
373                }
374
375                throw NO_MATCHING_VERIFIERS_EXCEPTION;
376        }
377
378
379        @Override
380        public JWTClaimsSet process(final EncryptedJWT encryptedJWT, final C context)
381                throws BadJOSEException, JOSEException {
382
383                if (getJWEKeySelector() == null) {
384                        // JWE key selector may have been deliberately omitted
385                        throw NO_JWE_KEY_SELECTOR_EXCEPTION;
386                }
387
388                if (getJWEDecrypterFactory() == null) {
389                        throw NO_JWE_DECRYPTER_FACTORY_EXCEPTION;
390                }
391
392                List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(encryptedJWT.getHeader(), context);
393
394                if (keyCandidates == null || keyCandidates.isEmpty()) {
395                        throw NO_JWE_KEY_CANDIDATES_EXCEPTION;
396                }
397
398                ListIterator<? extends Key> it = keyCandidates.listIterator();
399
400                while (it.hasNext()) {
401
402                        JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(encryptedJWT.getHeader(), it.next());
403
404                        if (decrypter == null) {
405                                continue;
406                        }
407
408                        try {
409                                encryptedJWT.decrypt(decrypter);
410
411                        } catch (JOSEException e) {
412
413                                if (it.hasNext()) {
414                                        // Try next key
415                                        continue;
416                                }
417
418                                // No more keys to try
419                                throw new BadJWEException("Encrypted JWT rejected: " + e.getMessage(), e);
420                        }
421
422                        if ("JWT".equalsIgnoreCase(encryptedJWT.getHeader().getContentType())) {
423
424                                // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2
425                                SignedJWT signedJWTPayload = encryptedJWT.getPayload().toSignedJWT();
426
427                                if (signedJWTPayload == null) {
428                                        // Cannot parse payload to signed JWT
429                                        throw INVALID_NESTED_JWT_EXCEPTION;
430                                }
431
432                                return process(signedJWTPayload, context);
433                        }
434
435                        JWTClaimsSet claimsSet = extractJWTClaimsSet(encryptedJWT);
436                        return verifyClaims(claimsSet, context);
437                }
438
439                throw NO_MATCHING_DECRYPTERS_EXCEPTION;
440        }
441}