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