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 2015-10-20
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 JWTClaimsVerifier claimsVerifier = new DefaultJWTClaimsVerifier();
123
124
125        @Override
126        public JWSKeySelector<C> getJWSKeySelector() {
127
128                return jwsKeySelector;
129        }
130
131
132        @Override
133        public void setJWSKeySelector(final JWSKeySelector<C> jwsKeySelector) {
134
135                this.jwsKeySelector = jwsKeySelector;
136        }
137
138
139        @Override
140        public JWEKeySelector<C> getJWEKeySelector() {
141
142                return jweKeySelector;
143        }
144
145
146        @Override
147        public void setJWEKeySelector(final JWEKeySelector<C> jweKeySelector) {
148
149                this.jweKeySelector = jweKeySelector;
150        }
151
152
153        @Override
154        public JWSVerifierFactory getJWSVerifierFactory() {
155
156                return jwsVerifierFactory;
157        }
158
159
160        @Override
161        public void setJWSVerifierFactory(final JWSVerifierFactory factory) {
162
163                jwsVerifierFactory = factory;
164        }
165
166
167        @Override
168        public JWEDecrypterFactory getJWEDecrypterFactory() {
169
170                return jweDecrypterFactory;
171        }
172
173
174        @Override
175        public void setJWEDecrypterFactory(final JWEDecrypterFactory factory) {
176
177                jweDecrypterFactory = factory;
178        }
179
180
181        @Override
182        public JWTClaimsVerifier getJWTClaimsVerifier() {
183
184                return claimsVerifier;
185        }
186
187
188        @Override
189        public void setJWTClaimsVerifier(final JWTClaimsVerifier claimsVerifier) {
190
191                this.claimsVerifier = claimsVerifier;
192        }
193
194
195        /**
196         * Verifies the claims of the specified JWT.
197         *
198         * @param jwt The JWT. Must be in a state which allows the claims to
199         *            be extracted.
200         *
201         * @return The JWT claims set.
202         *
203         * @throws BadJWTException If the JWT claims are invalid or rejected.
204         */
205        private JWTClaimsSet verifyAndReturnClaims(final JWT jwt)
206                throws BadJWTException {
207
208                JWTClaimsSet claimsSet;
209
210                try {
211                        claimsSet = jwt.getJWTClaimsSet();
212
213                } catch (ParseException e) {
214                        // Payload not a JSON object
215                        throw new BadJWTException(e.getMessage(), e);
216                }
217
218                if (getJWTClaimsVerifier() != null) {
219                        getJWTClaimsVerifier().verify(claimsSet);
220                }
221
222                return claimsSet;
223        }
224
225
226        @Override
227        public JWTClaimsSet process(final String jwtString, final C context)
228                throws ParseException, BadJOSEException, JOSEException {
229
230                return process(JWTParser.parse(jwtString), context);
231        }
232
233
234        @Override
235        public JWTClaimsSet process(final JWT jwt, final C context)
236                throws BadJOSEException, JOSEException {
237
238                if (jwt instanceof SignedJWT) {
239                        return process((SignedJWT)jwt, context);
240                }
241
242                if (jwt instanceof EncryptedJWT) {
243                        return process((EncryptedJWT)jwt, context);
244                }
245
246                if (jwt instanceof PlainJWT) {
247                        return process((PlainJWT)jwt, context);
248                }
249
250                // Should never happen
251                throw new JOSEException("Unexpected JWT object type: " + jwt.getClass());
252        }
253
254
255        @Override
256        public JWTClaimsSet process(final PlainJWT plainJWT, final C context)
257                throws BadJOSEException, JOSEException {
258
259                verifyAndReturnClaims(plainJWT); // just check claims, no return
260
261                throw PLAIN_JWT_REJECTED_EXCEPTION;
262        }
263
264
265        @Override
266        public JWTClaimsSet process(final SignedJWT signedJWT, final C context)
267                throws BadJOSEException, JOSEException {
268
269                if (getJWSKeySelector() == null) {
270                        // JWS key selector may have been deliberately omitted
271                        throw NO_JWS_KEY_SELECTOR_EXCEPTION;
272                }
273
274                if (getJWSVerifierFactory() == null) {
275                        throw NO_JWS_VERIFIER_FACTORY_EXCEPTION;
276                }
277
278                List<? extends Key> keyCandidates = getJWSKeySelector().selectJWSKeys(signedJWT.getHeader(), context);
279
280                if (keyCandidates == null || keyCandidates.isEmpty()) {
281                        throw NO_JWS_KEY_CANDIDATES_EXCEPTION;
282                }
283
284                ListIterator<? extends Key> it = keyCandidates.listIterator();
285
286                while (it.hasNext()) {
287
288                        JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(signedJWT.getHeader(), it.next());
289
290                        if (verifier == null) {
291                                continue;
292                        }
293
294                        final boolean validSignature = signedJWT.verify(verifier);
295
296                        if (validSignature) {
297                                return verifyAndReturnClaims(signedJWT);
298                        }
299
300                        if (! it.hasNext()) {
301                                // No more keys to try out
302                                throw INVALID_SIGNATURE;
303                        }
304                }
305
306                throw NO_MATCHING_VERIFIERS_EXCEPTION;
307        }
308
309
310        @Override
311        public JWTClaimsSet process(final EncryptedJWT encryptedJWT, final C context)
312                throws BadJOSEException, JOSEException {
313
314                if (getJWEKeySelector() == null) {
315                        // JWE key selector may have been deliberately omitted
316                        throw NO_JWE_KEY_SELECTOR_EXCEPTION;
317                }
318
319                if (getJWEDecrypterFactory() == null) {
320                        throw NO_JWE_DECRYPTER_FACTORY_EXCEPTION;
321                }
322
323                List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(encryptedJWT.getHeader(), context);
324
325                if (keyCandidates == null || keyCandidates.isEmpty()) {
326                        throw NO_JWE_KEY_CANDIDATES_EXCEPTION;
327                }
328
329                ListIterator<? extends Key> it = keyCandidates.listIterator();
330
331                while (it.hasNext()) {
332
333                        JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(encryptedJWT.getHeader(), it.next());
334
335                        if (decrypter == null) {
336                                continue;
337                        }
338
339                        try {
340                                encryptedJWT.decrypt(decrypter);
341
342                        } catch (JOSEException e) {
343
344                                if (it.hasNext()) {
345                                        // Try next key
346                                        continue;
347                                }
348
349                                // No more keys to try
350                                throw new BadJWEException("Encrypted JWT rejected: " + e.getMessage(), e);
351                        }
352
353                        if ("JWT".equalsIgnoreCase(encryptedJWT.getHeader().getContentType())) {
354
355                                // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2
356                                SignedJWT nestedJWT = encryptedJWT.getPayload().toSignedJWT();
357
358                                if (nestedJWT == null) {
359                                        // Cannot parse payload to signed JWT
360                                        throw INVALID_NESTED_JWT_EXCEPTION;
361                                }
362
363                                return process(nestedJWT, context);
364                        }
365
366                        return verifyAndReturnClaims(encryptedJWT);
367                }
368
369                throw NO_MATCHING_DECRYPTERS_EXCEPTION;
370        }
371}