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 * <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 * @version 2021-06-05
090 */
091public class DefaultJWTProcessor<C extends SecurityContext> implements ConfigurableJWTProcessor<C> {
092
093        
094        /**
095         * The JWS type verifier.
096         */
097        private JOSEObjectTypeVerifier<C> jwsTypeVerifier = DefaultJOSEObjectTypeVerifier.JWT;
098        
099        
100        /**
101         * The JWE type verifier.
102         */
103        private JOSEObjectTypeVerifier<C> jweTypeVerifier = DefaultJOSEObjectTypeVerifier.JWT;
104        
105
106        /**
107         * The JWS key selector.
108         */
109        private JWSKeySelector<C> jwsKeySelector;
110        
111        
112        /**
113         * The JWT claims aware JWS key selector, alternative to
114         * {@link #jwsKeySelector}.
115         */
116        private JWTClaimsSetAwareJWSKeySelector<C> claimsSetAwareJWSKeySelector;
117
118
119        /**
120         * The JWE key selector.
121         */
122        private JWEKeySelector<C> jweKeySelector;
123
124
125        /**
126         * The JWS verifier factory.
127         */
128        private JWSVerifierFactory jwsVerifierFactory = new DefaultJWSVerifierFactory();
129
130
131        /**
132         * The JWE decrypter factory.
133         */
134        private JWEDecrypterFactory jweDecrypterFactory = new DefaultJWEDecrypterFactory();
135
136
137        /**
138         * The claims verifier.
139         */
140        private JWTClaimsSetVerifier<C> claimsVerifier = new DefaultJWTClaimsVerifier<>(null, null);
141        
142        
143        @Override
144        public JOSEObjectTypeVerifier<C> getJWSTypeVerifier() {
145                
146                return jwsTypeVerifier;
147        }
148        
149        
150        @Override
151        public void setJWSTypeVerifier(final JOSEObjectTypeVerifier<C> jwsTypeVerifier) {
152        
153                this.jwsTypeVerifier = jwsTypeVerifier;
154        }
155        
156        
157        @Override
158        public JWSKeySelector<C> getJWSKeySelector() {
159
160                return jwsKeySelector;
161        }
162
163
164        @Override
165        public void setJWSKeySelector(final JWSKeySelector<C> jwsKeySelector) {
166
167                this.jwsKeySelector = jwsKeySelector;
168        }
169        
170        
171        @Override
172        public JWTClaimsSetAwareJWSKeySelector<C> getJWTClaimsSetAwareJWSKeySelector() {
173                
174                return claimsSetAwareJWSKeySelector;
175        }
176        
177        
178        @Override
179        public void setJWTClaimsSetAwareJWSKeySelector(final JWTClaimsSetAwareJWSKeySelector<C> jwsKeySelector) {
180        
181                this.claimsSetAwareJWSKeySelector = jwsKeySelector;
182        }
183        
184        
185        @Override
186        public JOSEObjectTypeVerifier<C> getJWETypeVerifier() {
187                
188                return jweTypeVerifier;
189        }
190        
191        
192        @Override
193        public void setJWETypeVerifier(final JOSEObjectTypeVerifier<C> jweTypeVerifier) {
194                
195                this.jweTypeVerifier = jweTypeVerifier;
196        }
197        
198        
199        @Override
200        public JWEKeySelector<C> getJWEKeySelector() {
201
202                return jweKeySelector;
203        }
204
205
206        @Override
207        public void setJWEKeySelector(final JWEKeySelector<C> jweKeySelector) {
208
209                this.jweKeySelector = jweKeySelector;
210        }
211
212
213        @Override
214        public JWSVerifierFactory getJWSVerifierFactory() {
215
216                return jwsVerifierFactory;
217        }
218
219
220        @Override
221        public void setJWSVerifierFactory(final JWSVerifierFactory factory) {
222
223                jwsVerifierFactory = factory;
224        }
225
226
227        @Override
228        public JWEDecrypterFactory getJWEDecrypterFactory() {
229
230                return jweDecrypterFactory;
231        }
232
233
234        @Override
235        public void setJWEDecrypterFactory(final JWEDecrypterFactory factory) {
236
237                jweDecrypterFactory = factory;
238        }
239        
240        
241        @Override
242        public JWTClaimsSetVerifier<C> getJWTClaimsSetVerifier() {
243                
244                return claimsVerifier;
245        }
246        
247        
248        @Override
249        public void setJWTClaimsSetVerifier(final JWTClaimsSetVerifier<C> claimsVerifier) {
250                
251                this.claimsVerifier = claimsVerifier;
252        }
253        
254        
255        private JWTClaimsSet extractJWTClaimsSet(final JWT jwt)
256                throws BadJWTException {
257                
258                try {
259                        return jwt.getJWTClaimsSet();
260                } catch (ParseException e) {
261                        // Payload not a JSON object
262                        throw new BadJWTException(e.getMessage(), e);
263                }
264        }
265
266
267        private JWTClaimsSet verifyClaims(final JWTClaimsSet claimsSet, final C context)
268                throws BadJWTException {
269                
270                if (getJWTClaimsSetVerifier() != null) {
271                        getJWTClaimsSetVerifier().verify(claimsSet, context);
272                }
273                return claimsSet;
274        }
275        
276        
277        private List<? extends Key> selectKeys(final JWSHeader header, final JWTClaimsSet claimsSet, final C context)
278                throws KeySourceException, BadJOSEException {
279                
280                if (getJWTClaimsSetAwareJWSKeySelector() != null) {
281                        return getJWTClaimsSetAwareJWSKeySelector().selectKeys(header, claimsSet, context);
282                } else if (getJWSKeySelector() != null) {
283                        return getJWSKeySelector().selectJWSKeys(header, context);
284                } else {
285                        throw new BadJOSEException("Signed JWT rejected: No JWS key selector is configured");
286                }
287        }
288
289
290        @Override
291        public JWTClaimsSet process(final String jwtString, final C context)
292                throws ParseException, BadJOSEException, JOSEException {
293
294                return process(JWTParser.parse(jwtString), context);
295        }
296
297
298        @Override
299        public JWTClaimsSet process(final JWT jwt, final C context)
300                throws BadJOSEException, JOSEException {
301
302                if (jwt instanceof SignedJWT) {
303                        return process((SignedJWT)jwt, context);
304                }
305
306                if (jwt instanceof EncryptedJWT) {
307                        return process((EncryptedJWT)jwt, context);
308                }
309
310                if (jwt instanceof PlainJWT) {
311                        return process((PlainJWT)jwt, context);
312                }
313
314                // Should never happen
315                throw new JOSEException("Unexpected JWT object type: " + jwt.getClass());
316        }
317
318
319        @Override
320        public JWTClaimsSet process(final PlainJWT plainJWT, final C context)
321                throws BadJOSEException, JOSEException {
322                
323                // JWS type verifier applies to unsecured JOSE as well
324                if (jwsTypeVerifier == null) {
325                        throw new BadJOSEException("Plain JWT rejected: No JWS header typ (type) verifier is configured");
326                }
327                jwsTypeVerifier.verify(plainJWT.getHeader().getType(), context);
328                
329                throw new BadJOSEException("Unsecured (plain) JWTs are rejected, extend class to handle");
330        }
331
332
333        @Override
334        public JWTClaimsSet process(final SignedJWT signedJWT, final C context)
335                throws BadJOSEException, JOSEException {
336                
337                if (jwsTypeVerifier == null) {
338                        throw new BadJOSEException("Signed JWT rejected: No JWS header typ (type) verifier is configured");
339                }
340                
341                jwsTypeVerifier.verify(signedJWT.getHeader().getType(), context);
342
343                if (getJWSKeySelector() == null && getJWTClaimsSetAwareJWSKeySelector() == null) {
344                        // JWS key selector may have been deliberately omitted
345                        throw new BadJOSEException("Signed JWT rejected: No JWS key selector is configured");
346                }
347
348                if (getJWSVerifierFactory() == null) {
349                        throw new JOSEException("No JWS verifier is configured");
350                }
351                
352                JWTClaimsSet claimsSet = extractJWTClaimsSet(signedJWT);
353
354                List<? extends Key> keyCandidates = selectKeys(signedJWT.getHeader(), claimsSet, context);
355
356                if (keyCandidates == null || keyCandidates.isEmpty()) {
357                        throw new BadJOSEException("Signed JWT rejected: Another algorithm expected, or no matching key(s) found");
358                }
359
360                ListIterator<? extends Key> it = keyCandidates.listIterator();
361
362                while (it.hasNext()) {
363
364                        JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(signedJWT.getHeader(), it.next());
365
366                        if (verifier == null) {
367                                continue;
368                        }
369
370                        final boolean validSignature = signedJWT.verify(verifier);
371
372                        if (validSignature) {
373                                return verifyClaims(claimsSet, context);
374                        }
375
376                        if (! it.hasNext()) {
377                                // No more keys to try out
378                                throw new BadJWSException("Signed JWT rejected: Invalid signature");
379                        }
380                }
381
382                throw new BadJOSEException("JWS object rejected: No matching verifier(s) found");
383        }
384
385
386        @Override
387        public JWTClaimsSet process(final EncryptedJWT encryptedJWT, final C context)
388                throws BadJOSEException, JOSEException {
389                
390                if (jweTypeVerifier == null) {
391                        throw new BadJOSEException("Encrypted JWT rejected: No JWE header typ (type) verifier is configured");
392                }
393                
394                jweTypeVerifier.verify(encryptedJWT.getHeader().getType(), context);
395
396                if (getJWEKeySelector() == null) {
397                        // JWE key selector may have been deliberately omitted
398                        throw new BadJOSEException("Encrypted JWT rejected: No JWE key selector is configured");
399                }
400
401                if (getJWEDecrypterFactory() == null) {
402                        throw new JOSEException("No JWE decrypter is configured");
403                }
404
405                List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(encryptedJWT.getHeader(), context);
406
407                if (keyCandidates == null || keyCandidates.isEmpty()) {
408                        throw new BadJOSEException("Encrypted JWT rejected: Another algorithm expected, or no matching key(s) found");
409                }
410
411                ListIterator<? extends Key> it = keyCandidates.listIterator();
412
413                while (it.hasNext()) {
414
415                        JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(encryptedJWT.getHeader(), it.next());
416
417                        if (decrypter == null) {
418                                continue;
419                        }
420
421                        try {
422                                encryptedJWT.decrypt(decrypter);
423
424                        } catch (JOSEException e) {
425
426                                if (it.hasNext()) {
427                                        // Try next key
428                                        continue;
429                                }
430
431                                // No more keys to try
432                                throw new BadJWEException("Encrypted JWT rejected: " + e.getMessage(), e);
433                        }
434
435                        if ("JWT".equalsIgnoreCase(encryptedJWT.getHeader().getContentType())) {
436
437                                // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2
438                                SignedJWT signedJWTPayload = encryptedJWT.getPayload().toSignedJWT();
439
440                                if (signedJWTPayload == null) {
441                                        // Cannot parse payload to signed JWT
442                                        throw new BadJWTException("The payload is not a nested signed JWT");
443                                }
444
445                                return process(signedJWTPayload, context);
446                        }
447
448                        JWTClaimsSet claimsSet = extractJWTClaimsSet(encryptedJWT);
449                        return verifyClaims(claimsSet, context);
450                }
451
452                throw new BadJOSEException("Encrypted JWT rejected: No matching decrypter(s) found");
453        }
454}