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