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