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