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}