001package com.nimbusds.jose.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.*; 011import com.nimbusds.jose.crypto.factories.DefaultJWEDecrypterFactory; 012import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory; 013import net.jcip.annotations.ThreadSafe; 014 015 016/** 017 * Default processor of {@link com.nimbusds.jose.PlainObject unsecured} 018 * (plain), {@link com.nimbusds.jose.JWSObject JWS} and 019 * {@link com.nimbusds.jose.JWEObject JWE} objects. 020 * 021 * <p>Must be configured with the following: 022 * 023 * <ol> 024 * <li>To verify JWS objects: A {@link JWSKeySelector JWS key selector} to 025 * determine the key candidate(s) for the signature verification. The key 026 * selection procedure is application-specific and may involve key ID 027 * lookup, a certificate check and / or other information supplied in the 028 * message {@link SecurityContext context}.</li> 029 * 030 * <li>To decrypt JWE objects: A {@link JWEKeySelector JWE key selector} to 031 * determine the key candidate(s) for decryption. The key selection 032 * procedure is application-specific and may involve key ID lookup, a 033 * certificate check and / or other information supplied in the message 034 * {@link SecurityContext context}.</li> 035 * </ol> 036 * 037 * <p>An optional context parameter is available to facilitate passing of 038 * additional data between the caller and the underlying selector of key 039 * candidates (in both directions). 040 * 041 * <p>See sections 6 of RFC 7515 (JWS) and RFC 7516 (JWE) for guidelines on key 042 * selection. 043 * 044 * <p>This processor comes with the default {@link DefaultJWSVerifierFactory 045 * JWS verifier factory} and the default {@link DefaultJWEDecrypterFactory 046 * JWE decrypter factory}; they can construct verifiers / decrypters for all 047 * standard JOSE algorithms implemented by the library. 048 * 049 * <p>Note that for security reasons this processor is hardwired to reject 050 * unsecured (plain) JOSE objects. Override the {@link #process(PlainObject, 051 * SecurityContext)} method if you need to handle unsecured JOSE objects as 052 * well. 053 * 054 * <p>To process JSON Web Tokens (JWTs) use the 055 * {@link com.nimbusds.jwt.proc.DefaultJWTProcessor} class. 056 * 057 * @author Vladimir Dzhuvinov 058 * @version 2016-06-15 059 */ 060@ThreadSafe 061public class DefaultJOSEProcessor<C extends SecurityContext> implements ConfigurableJOSEProcessor<C>{ 062 063 // Cache exceptions 064 private static final BadJOSEException PLAIN_JOSE_REJECTED_EXCEPTION = 065 new BadJOSEException("Unsecured (plain) JOSE objects are rejected, extend class to handle"); 066 private static final BadJOSEException NO_JWS_KEY_SELECTOR_EXCEPTION = 067 new BadJOSEException("JWS object rejected: No JWS key selector is configured"); 068 private static final BadJOSEException NO_JWE_KEY_SELECTOR_EXCEPTION = 069 new BadJOSEException("JWE object rejected: No JWE key selector is configured"); 070 private static final JOSEException NO_JWS_VERIFIER_FACTORY_EXCEPTION = 071 new JOSEException("No JWS verifier is configured"); 072 private static final JOSEException NO_JWE_DECRYPTER_FACTORY_EXCEPTION = 073 new JOSEException("No JWE decrypter is configured"); 074 private static final BadJOSEException NO_JWS_KEY_CANDIDATES_EXCEPTION = 075 new BadJOSEException("JWS object rejected: No matching key(s) found"); 076 private static final BadJOSEException NO_JWE_KEY_CANDIDATES_EXCEPTION = 077 new BadJOSEException("JWE object rejected: No matching key(s) found"); 078 private static final BadJOSEException INVALID_SIGNATURE = 079 new BadJWSException("JWS object rejected: Invalid signature"); 080 private static final BadJOSEException NO_MATCHING_VERIFIERS_EXCEPTION = 081 new BadJOSEException("JWS object rejected: No matching verifier(s) found"); 082 private static final BadJOSEException NO_MATCHING_DECRYPTERS_EXCEPTION = 083 new BadJOSEException("JWE object rejected: No matching decrypter(s) found"); 084 085 086 /** 087 * The JWS key selector. 088 */ 089 private JWSKeySelector<C> jwsKeySelector; 090 091 092 /** 093 * The JWE key selector. 094 */ 095 private JWEKeySelector<C> jweKeySelector; 096 097 098 /** 099 * The JWS verifier factory. 100 */ 101 private JWSVerifierFactory jwsVerifierFactory = new DefaultJWSVerifierFactory(); 102 103 104 /** 105 * The JWE decrypter factory. 106 */ 107 private JWEDecrypterFactory jweDecrypterFactory = new DefaultJWEDecrypterFactory(); 108 109 110 @Override 111 public JWSKeySelector<C> getJWSKeySelector() { 112 113 return jwsKeySelector; 114 } 115 116 117 @Override 118 public void setJWSKeySelector(final JWSKeySelector<C> jwsKeySelector) { 119 120 this.jwsKeySelector = jwsKeySelector; 121 } 122 123 124 @Override 125 public JWEKeySelector<C> getJWEKeySelector() { 126 127 return jweKeySelector; 128 } 129 130 131 @Override 132 public void setJWEKeySelector(final JWEKeySelector<C> jweKeySelector) { 133 134 this.jweKeySelector = jweKeySelector; 135 } 136 137 138 @Override 139 public JWSVerifierFactory getJWSVerifierFactory() { 140 141 return jwsVerifierFactory; 142 } 143 144 145 @Override 146 public void setJWSVerifierFactory(final JWSVerifierFactory factory) { 147 148 jwsVerifierFactory = factory; 149 } 150 151 152 @Override 153 public JWEDecrypterFactory getJWEDecrypterFactory() { 154 155 return jweDecrypterFactory; 156 } 157 158 159 @Override 160 public void setJWEDecrypterFactory(final JWEDecrypterFactory factory) { 161 162 jweDecrypterFactory = factory; 163 } 164 165 166 @Override 167 public Payload process(final String compactJOSE, final C context) 168 throws ParseException, BadJOSEException, JOSEException { 169 170 return process(JOSEObject.parse(compactJOSE), context); 171 } 172 173 174 @Override 175 public Payload process(final JOSEObject joseObject, final C context) 176 throws BadJOSEException, JOSEException { 177 178 if (joseObject instanceof JWSObject) { 179 return process((JWSObject)joseObject, context); 180 } 181 182 if (joseObject instanceof JWEObject) { 183 return process((JWEObject)joseObject, context); 184 } 185 186 if (joseObject instanceof PlainObject) { 187 return process((PlainObject)joseObject, context); 188 } 189 190 // Should never happen 191 throw new JOSEException("Unexpected JOSE object type: " + joseObject.getClass()); 192 } 193 194 195 @Override 196 public Payload process(final PlainObject plainObject, C context) 197 throws BadJOSEException { 198 199 throw PLAIN_JOSE_REJECTED_EXCEPTION; 200 } 201 202 203 @Override 204 public Payload process(final JWSObject jwsObject, C context) 205 throws BadJOSEException, JOSEException { 206 207 if (getJWSKeySelector() == null) { 208 // JWS key selector may have been deliberately omitted 209 throw NO_JWS_KEY_SELECTOR_EXCEPTION; 210 } 211 212 if (getJWSVerifierFactory() == null) { 213 throw NO_JWS_VERIFIER_FACTORY_EXCEPTION; 214 } 215 216 List<? extends Key> keyCandidates = getJWSKeySelector().selectJWSKeys(jwsObject.getHeader(), context); 217 218 if (keyCandidates == null || keyCandidates.isEmpty()) { 219 throw NO_JWS_KEY_CANDIDATES_EXCEPTION; 220 } 221 222 ListIterator<? extends Key> it = keyCandidates.listIterator(); 223 224 while (it.hasNext()) { 225 226 JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(jwsObject.getHeader(), it.next()); 227 228 if (verifier == null) { 229 continue; 230 } 231 232 final boolean validSignature = jwsObject.verify(verifier); 233 234 if (validSignature) { 235 return jwsObject.getPayload(); 236 } 237 238 if (! it.hasNext()) { 239 // No more keys to try out 240 throw INVALID_SIGNATURE; 241 } 242 } 243 244 throw NO_MATCHING_VERIFIERS_EXCEPTION; 245 } 246 247 248 @Override 249 public Payload process(final JWEObject jweObject, C context) 250 throws BadJOSEException, JOSEException { 251 252 if (getJWEKeySelector() == null) { 253 // JWE key selector may have been deliberately omitted 254 throw NO_JWE_KEY_SELECTOR_EXCEPTION; 255 } 256 257 if (getJWEDecrypterFactory() == null) { 258 throw NO_JWE_DECRYPTER_FACTORY_EXCEPTION; 259 } 260 261 List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(jweObject.getHeader(), context); 262 263 if (keyCandidates == null || keyCandidates.isEmpty()) { 264 throw NO_JWE_KEY_CANDIDATES_EXCEPTION; 265 } 266 267 ListIterator<? extends Key> it = keyCandidates.listIterator(); 268 269 while (it.hasNext()) { 270 271 JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(jweObject.getHeader(), it.next()); 272 273 if (decrypter == null) { 274 continue; 275 } 276 277 try { 278 jweObject.decrypt(decrypter); 279 280 } catch (JOSEException e) { 281 282 if (it.hasNext()) { 283 // Try next key 284 continue; 285 } 286 287 // No more keys to try 288 throw new BadJWEException("JWE object rejected: " + e.getMessage(), e); 289 } 290 291 if ("JWT".equalsIgnoreCase(jweObject.getHeader().getContentType())) { 292 293 // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2 294 JWSObject nestedJWS = jweObject.getPayload().toJWSObject(); 295 296 if (nestedJWS == null) { 297 // Cannot parse payload to JWS object, return original form 298 return jweObject.getPayload(); 299 } 300 301 return process(nestedJWS, context); 302 } 303 304 return jweObject.getPayload(); 305 } 306 307 throw NO_MATCHING_DECRYPTERS_EXCEPTION; 308 } 309}