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