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