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}