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