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}