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 2015-10-20 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 JWTClaimsVerifier claimsVerifier = new DefaultJWTClaimsVerifier(); 123 124 125 @Override 126 public JWSKeySelector<C> getJWSKeySelector() { 127 128 return jwsKeySelector; 129 } 130 131 132 @Override 133 public void setJWSKeySelector(final JWSKeySelector<C> jwsKeySelector) { 134 135 this.jwsKeySelector = jwsKeySelector; 136 } 137 138 139 @Override 140 public JWEKeySelector<C> getJWEKeySelector() { 141 142 return jweKeySelector; 143 } 144 145 146 @Override 147 public void setJWEKeySelector(final JWEKeySelector<C> jweKeySelector) { 148 149 this.jweKeySelector = jweKeySelector; 150 } 151 152 153 @Override 154 public JWSVerifierFactory getJWSVerifierFactory() { 155 156 return jwsVerifierFactory; 157 } 158 159 160 @Override 161 public void setJWSVerifierFactory(final JWSVerifierFactory factory) { 162 163 jwsVerifierFactory = factory; 164 } 165 166 167 @Override 168 public JWEDecrypterFactory getJWEDecrypterFactory() { 169 170 return jweDecrypterFactory; 171 } 172 173 174 @Override 175 public void setJWEDecrypterFactory(final JWEDecrypterFactory factory) { 176 177 jweDecrypterFactory = factory; 178 } 179 180 181 @Override 182 public JWTClaimsVerifier getJWTClaimsVerifier() { 183 184 return claimsVerifier; 185 } 186 187 188 @Override 189 public void setJWTClaimsVerifier(final JWTClaimsVerifier claimsVerifier) { 190 191 this.claimsVerifier = claimsVerifier; 192 } 193 194 195 /** 196 * Verifies the claims of the specified JWT. 197 * 198 * @param jwt The JWT. Must be in a state which allows the claims to 199 * be extracted. 200 * 201 * @return The JWT claims set. 202 * 203 * @throws BadJWTException If the JWT claims are invalid or rejected. 204 */ 205 private JWTClaimsSet verifyAndReturnClaims(final JWT jwt) 206 throws BadJWTException { 207 208 JWTClaimsSet claimsSet; 209 210 try { 211 claimsSet = jwt.getJWTClaimsSet(); 212 213 } catch (ParseException e) { 214 // Payload not a JSON object 215 throw new BadJWTException(e.getMessage(), e); 216 } 217 218 if (getJWTClaimsVerifier() != null) { 219 getJWTClaimsVerifier().verify(claimsSet); 220 } 221 222 return claimsSet; 223 } 224 225 226 @Override 227 public JWTClaimsSet process(final String jwtString, final C context) 228 throws ParseException, BadJOSEException, JOSEException { 229 230 return process(JWTParser.parse(jwtString), context); 231 } 232 233 234 @Override 235 public JWTClaimsSet process(final JWT jwt, final C context) 236 throws BadJOSEException, JOSEException { 237 238 if (jwt instanceof SignedJWT) { 239 return process((SignedJWT)jwt, context); 240 } 241 242 if (jwt instanceof EncryptedJWT) { 243 return process((EncryptedJWT)jwt, context); 244 } 245 246 if (jwt instanceof PlainJWT) { 247 return process((PlainJWT)jwt, context); 248 } 249 250 // Should never happen 251 throw new JOSEException("Unexpected JWT object type: " + jwt.getClass()); 252 } 253 254 255 @Override 256 public JWTClaimsSet process(final PlainJWT plainJWT, final C context) 257 throws BadJOSEException, JOSEException { 258 259 verifyAndReturnClaims(plainJWT); // just check claims, no return 260 261 throw PLAIN_JWT_REJECTED_EXCEPTION; 262 } 263 264 265 @Override 266 public JWTClaimsSet process(final SignedJWT signedJWT, final C context) 267 throws BadJOSEException, JOSEException { 268 269 if (getJWSKeySelector() == null) { 270 // JWS key selector may have been deliberately omitted 271 throw NO_JWS_KEY_SELECTOR_EXCEPTION; 272 } 273 274 if (getJWSVerifierFactory() == null) { 275 throw NO_JWS_VERIFIER_FACTORY_EXCEPTION; 276 } 277 278 List<? extends Key> keyCandidates = getJWSKeySelector().selectJWSKeys(signedJWT.getHeader(), context); 279 280 if (keyCandidates == null || keyCandidates.isEmpty()) { 281 throw NO_JWS_KEY_CANDIDATES_EXCEPTION; 282 } 283 284 ListIterator<? extends Key> it = keyCandidates.listIterator(); 285 286 while (it.hasNext()) { 287 288 JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(signedJWT.getHeader(), it.next()); 289 290 if (verifier == null) { 291 continue; 292 } 293 294 final boolean validSignature = signedJWT.verify(verifier); 295 296 if (validSignature) { 297 return verifyAndReturnClaims(signedJWT); 298 } 299 300 if (! it.hasNext()) { 301 // No more keys to try out 302 throw INVALID_SIGNATURE; 303 } 304 } 305 306 throw NO_MATCHING_VERIFIERS_EXCEPTION; 307 } 308 309 310 @Override 311 public JWTClaimsSet process(final EncryptedJWT encryptedJWT, final C context) 312 throws BadJOSEException, JOSEException { 313 314 if (getJWEKeySelector() == null) { 315 // JWE key selector may have been deliberately omitted 316 throw NO_JWE_KEY_SELECTOR_EXCEPTION; 317 } 318 319 if (getJWEDecrypterFactory() == null) { 320 throw NO_JWE_DECRYPTER_FACTORY_EXCEPTION; 321 } 322 323 List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(encryptedJWT.getHeader(), context); 324 325 if (keyCandidates == null || keyCandidates.isEmpty()) { 326 throw NO_JWE_KEY_CANDIDATES_EXCEPTION; 327 } 328 329 ListIterator<? extends Key> it = keyCandidates.listIterator(); 330 331 while (it.hasNext()) { 332 333 JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(encryptedJWT.getHeader(), it.next()); 334 335 if (decrypter == null) { 336 continue; 337 } 338 339 try { 340 encryptedJWT.decrypt(decrypter); 341 342 } catch (JOSEException e) { 343 344 if (it.hasNext()) { 345 // Try next key 346 continue; 347 } 348 349 // No more keys to try 350 throw new BadJWEException("Encrypted JWT rejected: " + e.getMessage(), e); 351 } 352 353 if ("JWT".equalsIgnoreCase(encryptedJWT.getHeader().getContentType())) { 354 355 // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2 356 SignedJWT nestedJWT = encryptedJWT.getPayload().toSignedJWT(); 357 358 if (nestedJWT == null) { 359 // Cannot parse payload to signed JWT 360 throw INVALID_NESTED_JWT_EXCEPTION; 361 } 362 363 return process(nestedJWT, context); 364 } 365 366 return verifyAndReturnClaims(encryptedJWT); 367 } 368 369 throw NO_MATCHING_DECRYPTERS_EXCEPTION; 370 } 371}