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