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-21 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 = getJWSKeySelector().selectJWSKeys(signedJWT.getHeader(), context); 280 281 if (keyCandidates == null || keyCandidates.isEmpty()) { 282 throw NO_JWS_KEY_CANDIDATES_EXCEPTION; 283 } 284 285 ListIterator<? extends Key> it = keyCandidates.listIterator(); 286 287 while (it.hasNext()) { 288 289 JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(signedJWT.getHeader(), it.next()); 290 291 if (verifier == null) { 292 continue; 293 } 294 295 final boolean validSignature = signedJWT.verify(verifier); 296 297 if (validSignature) { 298 return verifyAndReturnClaims(signedJWT); 299 } 300 301 if (! it.hasNext()) { 302 // No more keys to try out 303 throw INVALID_SIGNATURE; 304 } 305 } 306 307 throw NO_MATCHING_VERIFIERS_EXCEPTION; 308 } 309 310 311 @Override 312 public JWTClaimsSet process(final EncryptedJWT encryptedJWT, final C context) 313 throws BadJOSEException, JOSEException { 314 315 if (getJWEKeySelector() == null) { 316 // JWE key selector may have been deliberately omitted 317 throw NO_JWE_KEY_SELECTOR_EXCEPTION; 318 } 319 320 if (getJWEDecrypterFactory() == null) { 321 throw NO_JWE_DECRYPTER_FACTORY_EXCEPTION; 322 } 323 324 List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(encryptedJWT.getHeader(), context); 325 326 if (keyCandidates == null || keyCandidates.isEmpty()) { 327 throw NO_JWE_KEY_CANDIDATES_EXCEPTION; 328 } 329 330 ListIterator<? extends Key> it = keyCandidates.listIterator(); 331 332 while (it.hasNext()) { 333 334 JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(encryptedJWT.getHeader(), it.next()); 335 336 if (decrypter == null) { 337 continue; 338 } 339 340 try { 341 encryptedJWT.decrypt(decrypter); 342 343 } catch (JOSEException e) { 344 345 if (it.hasNext()) { 346 // Try next key 347 continue; 348 } 349 350 // No more keys to try 351 throw new BadJWEException("Encrypted JWT rejected: " + e.getMessage(), e); 352 } 353 354 if ("JWT".equalsIgnoreCase(encryptedJWT.getHeader().getContentType())) { 355 356 // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2 357 SignedJWT nestedJWT = encryptedJWT.getPayload().toSignedJWT(); 358 359 if (nestedJWT == null) { 360 // Cannot parse payload to signed JWT 361 throw INVALID_NESTED_JWT_EXCEPTION; 362 } 363 364 return process(nestedJWT, context); 365 } 366 367 return verifyAndReturnClaims(encryptedJWT); 368 } 369 370 throw NO_MATCHING_DECRYPTERS_EXCEPTION; 371 } 372}