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-08-27 063 */ 064public class DefaultJWTProcessor<C extends SecurityContext> 065 implements ConfigurableJWTProcessor<C> { 066 067 068 /** 069 * The JWS key selector. 070 */ 071 private JWSKeySelector<C> jwsKeySelector; 072 073 074 /** 075 * The JWE key selector. 076 */ 077 private JWEKeySelector<C> jweKeySelector; 078 079 080 /** 081 * The JWS verifier factory. 082 */ 083 private JWSVerifierFactory jwsVerifierFactory = new DefaultJWSVerifierFactory(); 084 085 086 /** 087 * The JWE decrypter factory. 088 */ 089 private JWEDecrypterFactory jweDecrypterFactory = new DefaultJWEDecrypterFactory(); 090 091 092 /** 093 * The claims verifier. 094 */ 095 private JWTClaimsVerifier claimsVerifier = new DefaultJWTClaimsVerifier(); 096 097 098 @Override 099 public JWSKeySelector<C> getJWSKeySelector() { 100 101 return jwsKeySelector; 102 } 103 104 105 @Override 106 public void setJWSKeySelector(final JWSKeySelector<C> jwsKeySelector) { 107 108 this.jwsKeySelector = jwsKeySelector; 109 } 110 111 112 @Override 113 public JWEKeySelector<C> getJWEKeySelector() { 114 115 return jweKeySelector; 116 } 117 118 119 @Override 120 public void setJWEKeySelector(final JWEKeySelector<C> jweKeySelector) { 121 122 this.jweKeySelector = jweKeySelector; 123 } 124 125 126 @Override 127 public JWSVerifierFactory getJWSVerifierFactory() { 128 129 return jwsVerifierFactory; 130 } 131 132 133 @Override 134 public void setJWSVerifierFactory(final JWSVerifierFactory factory) { 135 136 jwsVerifierFactory = factory; 137 } 138 139 140 @Override 141 public JWEDecrypterFactory getJWEDecrypterFactory() { 142 143 return jweDecrypterFactory; 144 } 145 146 147 @Override 148 public void setJWEDecrypterFactory(final JWEDecrypterFactory factory) { 149 150 jweDecrypterFactory = factory; 151 } 152 153 154 @Override 155 public JWTClaimsVerifier getJWTClaimsVerifier() { 156 157 return claimsVerifier; 158 } 159 160 161 @Override 162 public void setJWTClaimsVerifier(final JWTClaimsVerifier claimsVerifier) { 163 164 this.claimsVerifier = claimsVerifier; 165 } 166 167 168 /** 169 * Verifies the claims of the specified JWT. 170 * 171 * @param jwt The JWT. Must be in a state which allows the claims to 172 * be extracted. 173 * 174 * @return The JWT claims set. 175 * 176 * @throws BadJWTException If the JWT claims are invalid or rejected. 177 */ 178 private JWTClaimsSet verifyAndReturnClaims(final JWT jwt) 179 throws BadJWTException { 180 181 JWTClaimsSet claimsSet; 182 183 try { 184 claimsSet = jwt.getJWTClaimsSet(); 185 186 } catch (ParseException e) { 187 // Payload not a JSON object 188 throw new BadJWTException(e.getMessage(), e); 189 } 190 191 if (getJWTClaimsVerifier() != null) { 192 getJWTClaimsVerifier().verify(claimsSet); 193 } 194 195 return claimsSet; 196 } 197 198 199 @Override 200 public JWTClaimsSet process(final String jwtString, final C context) 201 throws ParseException, BadJOSEException, JOSEException { 202 203 return process(JWTParser.parse(jwtString), context); 204 } 205 206 207 @Override 208 public JWTClaimsSet process(final JWT jwt, final C context) 209 throws BadJOSEException, JOSEException { 210 211 if (jwt instanceof SignedJWT) { 212 return process((SignedJWT)jwt, context); 213 } 214 215 if (jwt instanceof EncryptedJWT) { 216 return process((EncryptedJWT)jwt, context); 217 } 218 219 if (jwt instanceof PlainJWT) { 220 return process((PlainJWT)jwt, context); 221 } 222 223 // Should never happen 224 throw new JOSEException("Unexpected JWT object type: " + jwt.getClass()); 225 } 226 227 228 @Override 229 public JWTClaimsSet process(final PlainJWT plainJWT, final C context) 230 throws BadJOSEException, JOSEException { 231 232 verifyAndReturnClaims(plainJWT); // just check claims, no return 233 234 throw new BadJOSEException("Unsecured (plain) JWTs are rejected, extend class to handle"); 235 } 236 237 238 @Override 239 public JWTClaimsSet process(final SignedJWT signedJWT, final C context) 240 throws BadJOSEException, JOSEException { 241 242 if (getJWSKeySelector() == null) { 243 // JWS key selector may have been deliberately omitted 244 throw new BadJOSEException("Signed JWT rejected: No JWS key selector is configured"); 245 } 246 247 if (getJWSVerifierFactory() == null) { 248 throw new JOSEException("No JWS verifier is configured"); 249 } 250 251 List<? extends Key> keyCandidates = getJWSKeySelector().selectJWSKeys(signedJWT.getHeader(), context); 252 253 if (keyCandidates == null || keyCandidates.isEmpty()) { 254 throw new BadJOSEException("Signed JWT rejected: No matching key(s) found"); 255 } 256 257 ListIterator<? extends Key> it = keyCandidates.listIterator(); 258 259 while (it.hasNext()) { 260 261 JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(signedJWT.getHeader(), it.next()); 262 263 if (verifier == null) { 264 continue; 265 } 266 267 final boolean validSignature = signedJWT.verify(verifier); 268 269 if (validSignature) { 270 return verifyAndReturnClaims(signedJWT); 271 } 272 273 if (! it.hasNext()) { 274 // No more keys to try out 275 throw new BadJWSException("Signed JWT rejected: Invalid signature"); 276 } 277 } 278 279 throw new BadJOSEException("JWS object rejected: No matching verifier(s) found"); 280 } 281 282 283 @Override 284 public JWTClaimsSet process(final EncryptedJWT encryptedJWT, final C context) 285 throws BadJOSEException, JOSEException { 286 287 if (getJWEKeySelector() == null) { 288 // JWE key selector may have been deliberately omitted 289 throw new BadJOSEException("Encrypted JWT rejected: No JWE key selector is configured"); 290 } 291 292 if (getJWEDecrypterFactory() == null) { 293 throw new JOSEException("No JWE decrypter is configured"); 294 } 295 296 List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(encryptedJWT.getHeader(), context); 297 298 if (keyCandidates == null || keyCandidates.isEmpty()) { 299 throw new BadJOSEException("Encrypted JWT rejected: No matching key(s) found"); 300 } 301 302 ListIterator<? extends Key> it = keyCandidates.listIterator(); 303 304 while (it.hasNext()) { 305 306 JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(encryptedJWT.getHeader(), it.next()); 307 308 if (decrypter == null) { 309 continue; 310 } 311 312 try { 313 encryptedJWT.decrypt(decrypter); 314 315 } catch (JOSEException e) { 316 317 if (it.hasNext()) { 318 // Try next key 319 continue; 320 } 321 322 // No more keys to try 323 throw new BadJWEException("Encrypted JWT rejected: " + e.getMessage(), e); 324 } 325 326 if ("JWT".equalsIgnoreCase(encryptedJWT.getHeader().getContentType())) { 327 328 // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2 329 SignedJWT nestedJWT = encryptedJWT.getPayload().toSignedJWT(); 330 331 if (nestedJWT == null) { 332 // Cannot parse payload to signed JWT 333 throw new BadJWTException("The payload is not a nested JWT"); 334 } 335 336 return process(nestedJWT, context); 337 } 338 339 return verifyAndReturnClaims(encryptedJWT); 340 } 341 342 throw new BadJOSEException("Encrypted JWT rejected: No matching decrypter(s) found"); 343 } 344}