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