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 java.security.Key; 022import java.text.ParseException; 023import java.util.List; 024import java.util.ListIterator; 025 026import com.nimbusds.jose.*; 027import com.nimbusds.jose.crypto.factories.DefaultJWEDecrypterFactory; 028import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory; 029import com.nimbusds.jose.proc.*; 030import com.nimbusds.jwt.*; 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 * @version 2021-06-05 090 */ 091public class DefaultJWTProcessor<C extends SecurityContext> implements ConfigurableJWTProcessor<C> { 092 093 094 /** 095 * The JWS type verifier. 096 */ 097 private JOSEObjectTypeVerifier<C> jwsTypeVerifier = DefaultJOSEObjectTypeVerifier.JWT; 098 099 100 /** 101 * The JWE type verifier. 102 */ 103 private JOSEObjectTypeVerifier<C> jweTypeVerifier = DefaultJOSEObjectTypeVerifier.JWT; 104 105 106 /** 107 * The JWS key selector. 108 */ 109 private JWSKeySelector<C> jwsKeySelector; 110 111 112 /** 113 * The JWT claims aware JWS key selector, alternative to 114 * {@link #jwsKeySelector}. 115 */ 116 private JWTClaimsSetAwareJWSKeySelector<C> claimsSetAwareJWSKeySelector; 117 118 119 /** 120 * The JWE key selector. 121 */ 122 private JWEKeySelector<C> jweKeySelector; 123 124 125 /** 126 * The JWS verifier factory. 127 */ 128 private JWSVerifierFactory jwsVerifierFactory = new DefaultJWSVerifierFactory(); 129 130 131 /** 132 * The JWE decrypter factory. 133 */ 134 private JWEDecrypterFactory jweDecrypterFactory = new DefaultJWEDecrypterFactory(); 135 136 137 /** 138 * The claims verifier. 139 */ 140 private JWTClaimsSetVerifier<C> claimsVerifier = new DefaultJWTClaimsVerifier<>(null, null); 141 142 143 @Override 144 public JOSEObjectTypeVerifier<C> getJWSTypeVerifier() { 145 146 return jwsTypeVerifier; 147 } 148 149 150 @Override 151 public void setJWSTypeVerifier(final JOSEObjectTypeVerifier<C> jwsTypeVerifier) { 152 153 this.jwsTypeVerifier = jwsTypeVerifier; 154 } 155 156 157 @Override 158 public JWSKeySelector<C> getJWSKeySelector() { 159 160 return jwsKeySelector; 161 } 162 163 164 @Override 165 public void setJWSKeySelector(final JWSKeySelector<C> jwsKeySelector) { 166 167 this.jwsKeySelector = jwsKeySelector; 168 } 169 170 171 @Override 172 public JWTClaimsSetAwareJWSKeySelector<C> getJWTClaimsSetAwareJWSKeySelector() { 173 174 return claimsSetAwareJWSKeySelector; 175 } 176 177 178 @Override 179 public void setJWTClaimsSetAwareJWSKeySelector(final JWTClaimsSetAwareJWSKeySelector<C> jwsKeySelector) { 180 181 this.claimsSetAwareJWSKeySelector = jwsKeySelector; 182 } 183 184 185 @Override 186 public JOSEObjectTypeVerifier<C> getJWETypeVerifier() { 187 188 return jweTypeVerifier; 189 } 190 191 192 @Override 193 public void setJWETypeVerifier(final JOSEObjectTypeVerifier<C> jweTypeVerifier) { 194 195 this.jweTypeVerifier = jweTypeVerifier; 196 } 197 198 199 @Override 200 public JWEKeySelector<C> getJWEKeySelector() { 201 202 return jweKeySelector; 203 } 204 205 206 @Override 207 public void setJWEKeySelector(final JWEKeySelector<C> jweKeySelector) { 208 209 this.jweKeySelector = jweKeySelector; 210 } 211 212 213 @Override 214 public JWSVerifierFactory getJWSVerifierFactory() { 215 216 return jwsVerifierFactory; 217 } 218 219 220 @Override 221 public void setJWSVerifierFactory(final JWSVerifierFactory factory) { 222 223 jwsVerifierFactory = factory; 224 } 225 226 227 @Override 228 public JWEDecrypterFactory getJWEDecrypterFactory() { 229 230 return jweDecrypterFactory; 231 } 232 233 234 @Override 235 public void setJWEDecrypterFactory(final JWEDecrypterFactory factory) { 236 237 jweDecrypterFactory = factory; 238 } 239 240 241 @Override 242 public JWTClaimsSetVerifier<C> getJWTClaimsSetVerifier() { 243 244 return claimsVerifier; 245 } 246 247 248 @Override 249 public void setJWTClaimsSetVerifier(final JWTClaimsSetVerifier<C> claimsVerifier) { 250 251 this.claimsVerifier = claimsVerifier; 252 } 253 254 255 private JWTClaimsSet extractJWTClaimsSet(final JWT jwt) 256 throws BadJWTException { 257 258 try { 259 return jwt.getJWTClaimsSet(); 260 } catch (ParseException e) { 261 // Payload not a JSON object 262 throw new BadJWTException(e.getMessage(), e); 263 } 264 } 265 266 267 private JWTClaimsSet verifyClaims(final JWTClaimsSet claimsSet, final C context) 268 throws BadJWTException { 269 270 if (getJWTClaimsSetVerifier() != null) { 271 getJWTClaimsSetVerifier().verify(claimsSet, context); 272 } 273 return claimsSet; 274 } 275 276 277 private List<? extends Key> selectKeys(final JWSHeader header, final JWTClaimsSet claimsSet, final C context) 278 throws KeySourceException, BadJOSEException { 279 280 if (getJWTClaimsSetAwareJWSKeySelector() != null) { 281 return getJWTClaimsSetAwareJWSKeySelector().selectKeys(header, claimsSet, context); 282 } else if (getJWSKeySelector() != null) { 283 return getJWSKeySelector().selectJWSKeys(header, context); 284 } else { 285 throw new BadJOSEException("Signed JWT rejected: No JWS key selector is configured"); 286 } 287 } 288 289 290 @Override 291 public JWTClaimsSet process(final String jwtString, final C context) 292 throws ParseException, BadJOSEException, JOSEException { 293 294 return process(JWTParser.parse(jwtString), context); 295 } 296 297 298 @Override 299 public JWTClaimsSet process(final JWT jwt, final C context) 300 throws BadJOSEException, JOSEException { 301 302 if (jwt instanceof SignedJWT) { 303 return process((SignedJWT)jwt, context); 304 } 305 306 if (jwt instanceof EncryptedJWT) { 307 return process((EncryptedJWT)jwt, context); 308 } 309 310 if (jwt instanceof PlainJWT) { 311 return process((PlainJWT)jwt, context); 312 } 313 314 // Should never happen 315 throw new JOSEException("Unexpected JWT object type: " + jwt.getClass()); 316 } 317 318 319 @Override 320 public JWTClaimsSet process(final PlainJWT plainJWT, final C context) 321 throws BadJOSEException, JOSEException { 322 323 // JWS type verifier applies to unsecured JOSE as well 324 if (jwsTypeVerifier == null) { 325 throw new BadJOSEException("Plain JWT rejected: No JWS header typ (type) verifier is configured"); 326 } 327 jwsTypeVerifier.verify(plainJWT.getHeader().getType(), context); 328 329 throw new BadJOSEException("Unsecured (plain) JWTs are rejected, extend class to handle"); 330 } 331 332 333 @Override 334 public JWTClaimsSet process(final SignedJWT signedJWT, final C context) 335 throws BadJOSEException, JOSEException { 336 337 if (jwsTypeVerifier == null) { 338 throw new BadJOSEException("Signed JWT rejected: No JWS header typ (type) verifier is configured"); 339 } 340 341 jwsTypeVerifier.verify(signedJWT.getHeader().getType(), context); 342 343 if (getJWSKeySelector() == null && getJWTClaimsSetAwareJWSKeySelector() == null) { 344 // JWS key selector may have been deliberately omitted 345 throw new BadJOSEException("Signed JWT rejected: No JWS key selector is configured"); 346 } 347 348 if (getJWSVerifierFactory() == null) { 349 throw new JOSEException("No JWS verifier is configured"); 350 } 351 352 JWTClaimsSet claimsSet = extractJWTClaimsSet(signedJWT); 353 354 List<? extends Key> keyCandidates = selectKeys(signedJWT.getHeader(), claimsSet, context); 355 356 if (keyCandidates == null || keyCandidates.isEmpty()) { 357 throw new BadJOSEException("Signed JWT rejected: Another algorithm expected, or no matching key(s) found"); 358 } 359 360 ListIterator<? extends Key> it = keyCandidates.listIterator(); 361 362 while (it.hasNext()) { 363 364 JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(signedJWT.getHeader(), it.next()); 365 366 if (verifier == null) { 367 continue; 368 } 369 370 final boolean validSignature = signedJWT.verify(verifier); 371 372 if (validSignature) { 373 return verifyClaims(claimsSet, context); 374 } 375 376 if (! it.hasNext()) { 377 // No more keys to try out 378 throw new BadJWSException("Signed JWT rejected: Invalid signature"); 379 } 380 } 381 382 throw new BadJOSEException("JWS object rejected: No matching verifier(s) found"); 383 } 384 385 386 @Override 387 public JWTClaimsSet process(final EncryptedJWT encryptedJWT, final C context) 388 throws BadJOSEException, JOSEException { 389 390 if (jweTypeVerifier == null) { 391 throw new BadJOSEException("Encrypted JWT rejected: No JWE header typ (type) verifier is configured"); 392 } 393 394 jweTypeVerifier.verify(encryptedJWT.getHeader().getType(), context); 395 396 if (getJWEKeySelector() == null) { 397 // JWE key selector may have been deliberately omitted 398 throw new BadJOSEException("Encrypted JWT rejected: No JWE key selector is configured"); 399 } 400 401 if (getJWEDecrypterFactory() == null) { 402 throw new JOSEException("No JWE decrypter is configured"); 403 } 404 405 List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(encryptedJWT.getHeader(), context); 406 407 if (keyCandidates == null || keyCandidates.isEmpty()) { 408 throw new BadJOSEException("Encrypted JWT rejected: Another algorithm expected, or no matching key(s) found"); 409 } 410 411 ListIterator<? extends Key> it = keyCandidates.listIterator(); 412 413 while (it.hasNext()) { 414 415 JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(encryptedJWT.getHeader(), it.next()); 416 417 if (decrypter == null) { 418 continue; 419 } 420 421 try { 422 encryptedJWT.decrypt(decrypter); 423 424 } catch (JOSEException e) { 425 426 if (it.hasNext()) { 427 // Try next key 428 continue; 429 } 430 431 // No more keys to try 432 throw new BadJWEException("Encrypted JWT rejected: " + e.getMessage(), e); 433 } 434 435 if ("JWT".equalsIgnoreCase(encryptedJWT.getHeader().getContentType())) { 436 437 // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2 438 SignedJWT signedJWTPayload = encryptedJWT.getPayload().toSignedJWT(); 439 440 if (signedJWTPayload == null) { 441 // Cannot parse payload to signed JWT 442 throw new BadJWTException("The payload is not a nested signed JWT"); 443 } 444 445 return process(signedJWTPayload, context); 446 } 447 448 JWTClaimsSet claimsSet = extractJWTClaimsSet(encryptedJWT); 449 return verifyClaims(claimsSet, context); 450 } 451 452 throw new BadJOSEException("Encrypted JWT rejected: No matching decrypter(s) found"); 453 } 454}