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