001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2019, 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 com.nimbusds.jose.*; 022import com.nimbusds.jose.crypto.factories.DefaultJWEDecrypterFactory; 023import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory; 024import com.nimbusds.jose.proc.*; 025import com.nimbusds.jwt.*; 026 027import java.security.Key; 028import java.text.ParseException; 029import java.util.List; 030import java.util.ListIterator; 031 032 033/** 034 * Default processor of {@link com.nimbusds.jwt.PlainJWT unsecured} (plain), 035 * {@link com.nimbusds.jwt.SignedJWT signed} and 036 * {@link com.nimbusds.jwt.EncryptedJWT encrypted} JSON Web Tokens (JWTs). 037 * 038 * <p>Must be configured with the following: 039 * 040 * <ul> 041 * <li>To process signed JWTs: A {@link #setJWSKeySelector JWS key 042 * selector} using the header or the {@link JWTClaimsSetAwareJWSKeySelector 043 * header and claims set} to suggest key candidate(s) for the signature 044 * verification. The key selection procedure is application-specific and 045 * may involve key ID lookup, a certificate check and / or some 046 * {@link SecurityContext context}.</li> 047 * 048 * <li>To process encrypted JWTs: A {@link #setJWEKeySelector JWE key 049 * selector} using the header to suggest key candidate(s) for decryption. 050 * The key selection procedure is application-specific and may involve key 051 * ID lookup, a certificate check and / or some {@link SecurityContext 052 * context}.</li> 053 * </ul> 054 * 055 * <p>An optional {@link SecurityContext context} parameter is available to 056 * facilitate passing of additional data between the caller and the underlying 057 * selector of key candidates (in both directions). 058 * 059 * <p>See sections 6 of RFC 7515 (JWS) and RFC 7516 (JWE) for guidelines on key 060 * selection. 061 * 062 * <p>This processor is configured with a standard header "typ" (type) 063 * parameter {@link DefaultJOSEObjectTypeVerifier#JWT verifier} which expects 064 * the signed, encrypted and plain (unsecured) JWTs to have the type header 065 * omitted or set to {@link JOSEObjectType#JWT JWT}. To accept other "typ" 066 * values pass an appropriately configured JWS and / or JWE 067 * {@link DefaultJOSEObjectTypeVerifier type verifier}. 068 * 069 * <p>This processor comes with the default {@link DefaultJWSVerifierFactory 070 * JWS verifier factory} and the default {@link DefaultJWEDecrypterFactory 071 * JWE decrypter factory}; they can construct verifiers / decrypters for all 072 * standard JOSE algorithms implemented by the library. 073 * 074 * <p>Note that for security reasons this processor is hardwired to reject 075 * unsecured (plain) JWTs. Override the {@link #process(PlainJWT, SecurityContext)} 076 * if you need to handle plain JWTs. 077 * 078 * <p>A {@link DefaultJWTClaimsVerifier default JWT claims verifier} is 079 * provided, to perform a minimal check of the claims after a successful JWS 080 * verification / JWE decryption. It checks the token expiration (exp) and 081 * not-before (nbf) timestamps if these are present. The default JWT claims 082 * verifier may be extended to perform additional checks, such as issuer and 083 * subject acceptance. 084 * 085 * <p>To process generic JOSE objects (with arbitrary payloads) use the 086 * {@link com.nimbusds.jose.proc.DefaultJOSEProcessor} class. 087 * 088 * @author Vladimir Dzhuvinov 089 * @author Misagh Moayyed 090 * @version 2024-01-15 091 */ 092public class DefaultJWTProcessor<C extends SecurityContext> implements ConfigurableJWTProcessor<C> { 093 094 095 /** 096 * The JWS type verifier. 097 */ 098 private JOSEObjectTypeVerifier<C> jwsTypeVerifier = DefaultJOSEObjectTypeVerifier.JWT; 099 100 101 /** 102 * The JWE type verifier. 103 */ 104 private JOSEObjectTypeVerifier<C> jweTypeVerifier = DefaultJOSEObjectTypeVerifier.JWT; 105 106 107 /** 108 * The JWS key selector. 109 */ 110 private JWSKeySelector<C> jwsKeySelector; 111 112 113 /** 114 * The JWT claims aware JWS key selector, alternative to 115 * {@link #jwsKeySelector}. 116 */ 117 private JWTClaimsSetAwareJWSKeySelector<C> claimsSetAwareJWSKeySelector; 118 119 120 /** 121 * The JWE key selector. 122 */ 123 private JWEKeySelector<C> jweKeySelector; 124 125 126 /** 127 * The JWS verifier factory. 128 */ 129 private JWSVerifierFactory jwsVerifierFactory = new DefaultJWSVerifierFactory(); 130 131 132 /** 133 * The JWE decrypter factory. 134 */ 135 private JWEDecrypterFactory jweDecrypterFactory = new DefaultJWEDecrypterFactory(); 136 137 138 /** 139 * The claims verifier. 140 */ 141 private JWTClaimsSetVerifier<C> claimsVerifier = new DefaultJWTClaimsVerifier<>(null, null); 142 143 144 @Override 145 public JOSEObjectTypeVerifier<C> getJWSTypeVerifier() { 146 147 return jwsTypeVerifier; 148 } 149 150 151 @Override 152 public void setJWSTypeVerifier(final JOSEObjectTypeVerifier<C> jwsTypeVerifier) { 153 154 this.jwsTypeVerifier = jwsTypeVerifier; 155 } 156 157 158 @Override 159 public JWSKeySelector<C> getJWSKeySelector() { 160 161 return jwsKeySelector; 162 } 163 164 165 @Override 166 public void setJWSKeySelector(final JWSKeySelector<C> jwsKeySelector) { 167 168 this.jwsKeySelector = jwsKeySelector; 169 } 170 171 172 @Override 173 public JWTClaimsSetAwareJWSKeySelector<C> getJWTClaimsSetAwareJWSKeySelector() { 174 175 return claimsSetAwareJWSKeySelector; 176 } 177 178 179 @Override 180 public void setJWTClaimsSetAwareJWSKeySelector(final JWTClaimsSetAwareJWSKeySelector<C> jwsKeySelector) { 181 182 this.claimsSetAwareJWSKeySelector = jwsKeySelector; 183 } 184 185 186 @Override 187 public JOSEObjectTypeVerifier<C> getJWETypeVerifier() { 188 189 return jweTypeVerifier; 190 } 191 192 193 @Override 194 public void setJWETypeVerifier(final JOSEObjectTypeVerifier<C> jweTypeVerifier) { 195 196 this.jweTypeVerifier = jweTypeVerifier; 197 } 198 199 200 @Override 201 public JWEKeySelector<C> getJWEKeySelector() { 202 203 return jweKeySelector; 204 } 205 206 207 @Override 208 public void setJWEKeySelector(final JWEKeySelector<C> jweKeySelector) { 209 210 this.jweKeySelector = jweKeySelector; 211 } 212 213 214 @Override 215 public JWSVerifierFactory getJWSVerifierFactory() { 216 217 return jwsVerifierFactory; 218 } 219 220 221 @Override 222 public void setJWSVerifierFactory(final JWSVerifierFactory factory) { 223 224 jwsVerifierFactory = factory; 225 } 226 227 228 @Override 229 public JWEDecrypterFactory getJWEDecrypterFactory() { 230 231 return jweDecrypterFactory; 232 } 233 234 235 @Override 236 public void setJWEDecrypterFactory(final JWEDecrypterFactory factory) { 237 238 jweDecrypterFactory = factory; 239 } 240 241 242 @Override 243 public JWTClaimsSetVerifier<C> getJWTClaimsSetVerifier() { 244 245 return claimsVerifier; 246 } 247 248 249 @Override 250 public void setJWTClaimsSetVerifier(final JWTClaimsSetVerifier<C> claimsVerifier) { 251 252 this.claimsVerifier = claimsVerifier; 253 } 254 255 256 /** 257 * Extracts the claims set from the specified JWT. 258 * 259 * @param jwt The JWT. Must not be {@code null}. 260 * 261 * @return The JWT claims set. 262 * 263 * @throws BadJWTException If the payload of the JWT doesn't represent 264 * a valid JSON object and a JWT claims set. 265 */ 266 protected JWTClaimsSet extractJWTClaimsSet(final JWT jwt) 267 throws BadJWTException { 268 269 try { 270 return jwt.getJWTClaimsSet(); 271 } catch (ParseException e) { 272 // Payload not a JSON object 273 throw new BadJWTException(e.getMessage(), e); 274 } 275 } 276 277 278 /** 279 * Verifies the specified JWT claims set. 280 * 281 * @param claimsSet The JWT claims set. Must not be {@code null}. 282 * @param context Optional context, {@code null} if not required. 283 * 284 * @return The verified JWT claims set. 285 * 286 * @throws BadJWTException If the JWT claims set is rejected. 287 */ 288 protected JWTClaimsSet verifyJWTClaimsSet(final JWTClaimsSet claimsSet, final C context) 289 throws BadJWTException { 290 291 if (getJWTClaimsSetVerifier() != null) { 292 getJWTClaimsSetVerifier().verify(claimsSet, context); 293 } 294 return claimsSet; 295 } 296 297 298 /** 299 * Selects key candidates for verifying a signed JWT. 300 * 301 * @param header The JWS header. Must not be {@code null}. 302 * @param claimsSet The JWT claims set (not verified). Must not be 303 * {@code null}. 304 * @param context Optional context, {@code null} if not required. 305 * 306 * @return The key candidates in trial order, empty list if none. 307 * 308 * @throws KeySourceException If a key sourcing exception is 309 * encountered, e.g. on remote JWK 310 * retrieval. 311 * @throws BadJOSEException If an internal processing exception is 312 * encountered. 313 */ 314 protected List<? extends Key> selectKeys(final JWSHeader header, final JWTClaimsSet claimsSet, final C context) 315 throws KeySourceException, BadJOSEException { 316 317 if (getJWTClaimsSetAwareJWSKeySelector() != null) { 318 return getJWTClaimsSetAwareJWSKeySelector().selectKeys(header, claimsSet, context); 319 } else if (getJWSKeySelector() != null) { 320 return getJWSKeySelector().selectJWSKeys(header, context); 321 } else { 322 throw new BadJOSEException("Signed JWT rejected: No JWS key selector is configured"); 323 } 324 } 325 326 327 @Override 328 public JWTClaimsSet process(final String jwtString, final C context) 329 throws ParseException, BadJOSEException, JOSEException { 330 331 return process(JWTParser.parse(jwtString), context); 332 } 333 334 335 @Override 336 public JWTClaimsSet process(final JWT jwt, final C context) 337 throws BadJOSEException, JOSEException { 338 339 if (jwt instanceof SignedJWT) { 340 return process((SignedJWT)jwt, context); 341 } 342 343 if (jwt instanceof EncryptedJWT) { 344 return process((EncryptedJWT)jwt, context); 345 } 346 347 if (jwt instanceof PlainJWT) { 348 return process((PlainJWT)jwt, context); 349 } 350 351 // Should never happen 352 throw new JOSEException("Unexpected JWT object type: " + jwt.getClass()); 353 } 354 355 356 @Override 357 public JWTClaimsSet process(final PlainJWT plainJWT, final C context) 358 throws BadJOSEException, JOSEException { 359 360 // JWS type verifier applies to unsecured JOSE as well 361 if (jwsTypeVerifier == null) { 362 throw new BadJOSEException("Plain JWT rejected: No JWS header typ (type) verifier is configured"); 363 } 364 jwsTypeVerifier.verify(plainJWT.getHeader().getType(), context); 365 366 throw new BadJOSEException("Unsecured (plain) JWTs are rejected, extend class to handle"); 367 } 368 369 370 @Override 371 public JWTClaimsSet process(final SignedJWT signedJWT, final C context) 372 throws BadJOSEException, JOSEException { 373 374 if (jwsTypeVerifier == null) { 375 throw new BadJOSEException("Signed JWT rejected: No JWS header typ (type) verifier is configured"); 376 } 377 378 jwsTypeVerifier.verify(signedJWT.getHeader().getType(), context); 379 380 if (getJWSKeySelector() == null && getJWTClaimsSetAwareJWSKeySelector() == null) { 381 // JWS key selector may have been deliberately omitted 382 throw new BadJOSEException("Signed JWT rejected: No JWS key selector is configured"); 383 } 384 385 if (getJWSVerifierFactory() == null) { 386 throw new JOSEException("No JWS verifier is configured"); 387 } 388 389 JWTClaimsSet claimsSet = extractJWTClaimsSet(signedJWT); 390 391 List<? extends Key> keyCandidates = selectKeys(signedJWT.getHeader(), claimsSet, context); 392 393 if (keyCandidates == null || keyCandidates.isEmpty()) { 394 throw new BadJOSEException("Signed JWT rejected: Another algorithm expected, or no matching key(s) found"); 395 } 396 397 ListIterator<? extends Key> it = keyCandidates.listIterator(); 398 399 while (it.hasNext()) { 400 401 JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(signedJWT.getHeader(), it.next()); 402 403 if (verifier == null) { 404 continue; 405 } 406 407 final boolean validSignature = signedJWT.verify(verifier); 408 409 if (validSignature) { 410 return verifyJWTClaimsSet(claimsSet, context); 411 } 412 413 if (! it.hasNext()) { 414 // No more keys to try out 415 throw new BadJWSException("Signed JWT rejected: Invalid signature"); 416 } 417 } 418 419 throw new BadJOSEException("JWS object rejected: No matching verifier(s) found"); 420 } 421 422 423 @Override 424 public JWTClaimsSet process(final EncryptedJWT encryptedJWT, final C context) 425 throws BadJOSEException, JOSEException { 426 427 if (jweTypeVerifier == null) { 428 throw new BadJOSEException("Encrypted JWT rejected: No JWE header typ (type) verifier is configured"); 429 } 430 431 jweTypeVerifier.verify(encryptedJWT.getHeader().getType(), context); 432 433 if (getJWEKeySelector() == null) { 434 // JWE key selector may have been deliberately omitted 435 throw new BadJOSEException("Encrypted JWT rejected: No JWE key selector is configured"); 436 } 437 438 if (getJWEDecrypterFactory() == null) { 439 throw new JOSEException("No JWE decrypter is configured"); 440 } 441 442 List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(encryptedJWT.getHeader(), context); 443 444 if (keyCandidates == null || keyCandidates.isEmpty()) { 445 throw new BadJOSEException("Encrypted JWT rejected: Another algorithm expected, or no matching key(s) found"); 446 } 447 448 ListIterator<? extends Key> it = keyCandidates.listIterator(); 449 450 while (it.hasNext()) { 451 452 JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(encryptedJWT.getHeader(), it.next()); 453 454 if (decrypter == null) { 455 continue; 456 } 457 458 try { 459 encryptedJWT.decrypt(decrypter); 460 461 } catch (JOSEException e) { 462 463 if (it.hasNext()) { 464 // Try next key 465 continue; 466 } 467 468 // No more keys to try 469 throw new BadJWEException("Encrypted JWT rejected: " + e.getMessage(), e); 470 } 471 472 if ("JWT".equalsIgnoreCase(encryptedJWT.getHeader().getContentType())) { 473 474 // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2 475 SignedJWT signedJWTPayload = encryptedJWT.getPayload().toSignedJWT(); 476 477 if (signedJWTPayload == null) { 478 // Cannot parse payload to signed JWT 479 throw new BadJWTException("The payload is not a nested signed JWT"); 480 } 481 482 return process(signedJWTPayload, context); 483 } 484 485 JWTClaimsSet claimsSet = extractJWTClaimsSet(encryptedJWT); 486 return verifyJWTClaimsSet(claimsSet, context); 487 } 488 489 throw new BadJOSEException("Encrypted JWT rejected: No matching decrypter(s) found"); 490 } 491}