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