001package com.nimbusds.jose.proc; 002 003 004import java.security.Key; 005import java.text.ParseException; 006import java.util.List; 007import java.util.ListIterator; 008 009import net.jcip.annotations.ThreadSafe; 010 011import com.nimbusds.jose.*; 012 013 014/** 015 * Default processor of received {@link com.nimbusds.jose.JOSEObject}s. 016 * 017 * <p>Must be supplied with a {@link JWSKeySelector JWS key selector} to 018 * determine the key candidate(s) for the signature verification. The exact key 019 * selection procedure is application-specific and may involve key ID lookup, a 020 * certificate check and / or other information supplied in the message 021 * {@link SecurityContext context}. 022 * 023 * <p>Similarly, the processor must be supplied with a {@link JWEKeySelector 024 * JWE key selector} if JWE messages are expected to be processed. 025 * 026 * <p>See sections 6 of RFC 7515 (JWS) and RFC 7516 (JWE) for guidelines on key 027 * selection. 028 * 029 * <p>This processor comes with the default {@link DefaultJWSVerifierFactory 030 * JWS verifier factory} and the default {@link DefaultJWEDecrypterFactory 031 * JWE decrypter factory}; they can construct verifiers / decrypters for all 032 * standard JOSE algorithms implemented by the library. 033 * 034 * <p>Note that for security reasons this processor is hardwired to reject 035 * unsecured (plain) JOSE objects. Override the {@link #process(PlainObject, 036 * SecurityContext)} if you need to handle plain JOSE objects as well. 037 * 038 * <p>To process JSON Web Tokens (JWTs) use the 039 * {@link com.nimbusds.jwt.proc.DefaultJWTProcessor} class. 040 * 041 * @author Vladimir Dzhuvinov 042 * @version 2015-07-01 043 */ 044@ThreadSafe 045public class DefaultJOSEProcessor<C extends SecurityContext> 046 extends BaseJOSEProcessor<C> 047 implements JOSEProcessor<Payload, C>{ 048 049 @Override 050 public Payload process(final String compactJOSE, final C context) 051 throws ParseException, BadJOSEException, JOSEException { 052 053 return process(JOSEObject.parse(compactJOSE), context); 054 } 055 056 057 @Override 058 public Payload process(final JOSEObject joseObject, final C context) 059 throws BadJOSEException, JOSEException { 060 061 if (joseObject instanceof JWSObject) { 062 return process((JWSObject)joseObject, context); 063 } 064 065 if (joseObject instanceof JWEObject) { 066 return process((JWEObject)joseObject, context); 067 } 068 069 if (joseObject instanceof PlainObject) { 070 return process((PlainObject)joseObject, context); 071 } 072 073 // Should never happen 074 throw new JOSEException("Unexpected JOSE object type: " + joseObject.getClass()); 075 } 076 077 078 @Override 079 public Payload process(final PlainObject plainObject, C context) 080 throws BadJOSEException { 081 082 throw new BadJOSEException("Unsecured (plain) JOSE objects are rejected, extend class to handle"); 083 } 084 085 086 @Override 087 public Payload process(final JWSObject jwsObject, C context) 088 throws BadJOSEException, JOSEException { 089 090 if (getJWSKeySelector() == null) { 091 // JWS key selector may have been deliberately omitted 092 throw new BadJOSEException("JWS object rejected: No JWS key selector is configured"); 093 } 094 095 if (getJWSVerifierFactory() == null) { 096 throw new JOSEException("No JWS verifier is configured"); 097 } 098 099 List<? extends Key> keyCandidates = getJWSKeySelector().selectJWSKeys(jwsObject.getHeader(), context); 100 101 if (keyCandidates == null || keyCandidates.isEmpty()) { 102 throw new BadJOSEException("JWS object rejected: No matching key(s) found"); 103 } 104 105 ListIterator<? extends Key> it = keyCandidates.listIterator(); 106 107 while (it.hasNext()) { 108 109 JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(jwsObject.getHeader(), it.next()); 110 111 if (verifier == null) { 112 continue; 113 } 114 115 final boolean validSignature = jwsObject.verify(verifier); 116 117 if (validSignature) { 118 return jwsObject.getPayload(); 119 } 120 121 if (! it.hasNext()) { 122 // No more keys to try out 123 throw new BadJWSException("JWS object rejected: Invalid signature"); 124 } 125 } 126 127 throw new BadJOSEException("JWS object rejected: No matching verifier(s) found"); 128 } 129 130 131 @Override 132 public Payload process(final JWEObject jweObject, C context) 133 throws BadJOSEException, JOSEException { 134 135 if (getJWEKeySelector() == null) { 136 // JWE key selector may have been deliberately omitted 137 throw new BadJOSEException("JWE object rejected: No JWE key selector is configured"); 138 } 139 140 if (getJWEDecrypterFactory() == null) { 141 throw new JOSEException("No JWE decrypter is configured"); 142 } 143 144 List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(jweObject.getHeader(), context); 145 146 if (keyCandidates == null || keyCandidates.isEmpty()) { 147 throw new BadJOSEException("JWE object rejected: No matching key(s) found"); 148 } 149 150 ListIterator<? extends Key> it = keyCandidates.listIterator(); 151 152 while (it.hasNext()) { 153 154 JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(jweObject.getHeader(), it.next()); 155 156 if (decrypter == null) { 157 continue; 158 } 159 160 try { 161 jweObject.decrypt(decrypter); 162 163 } catch (JOSEException e) { 164 165 if (it.hasNext()) { 166 // Try next key 167 continue; 168 } 169 170 // No more keys to try 171 throw new BadJWEException("JWE object rejected: " + e.getMessage(), e); 172 } 173 174 if ("JWT".equalsIgnoreCase(jweObject.getHeader().getContentType())) { 175 176 // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2 177 JWSObject nestedJWS = jweObject.getPayload().toJWSObject(); 178 179 if (nestedJWS == null) { 180 // Cannot parse payload to JWS object, return original form 181 return jweObject.getPayload(); 182 } 183 184 return process(nestedJWS, context); 185 } 186 187 return jweObject.getPayload(); 188 } 189 190 throw new BadJOSEException("JWE object rejected: No matching decrypter(s) found"); 191 } 192}