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