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