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