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