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-08-22
055 */
056@ThreadSafe
057public class DefaultJOSEProcessor<C extends SecurityContext> implements ConfigurableJOSEProcessor<C>{
058
059
060        /**
061         * The JWS key selector.
062         */
063        private JWSKeySelector<C> jwsKeySelector;
064
065
066        /**
067         * The JWE key selector.
068         */
069        private JWEKeySelector<C> jweKeySelector;
070
071
072        /**
073         * The JWS verifier factory.
074         */
075        private JWSVerifierFactory jwsVerifierFactory = new DefaultJWSVerifierFactory();
076
077
078        /**
079         * The JWE decrypter factory.
080         */
081        private JWEDecrypterFactory jweDecrypterFactory = new DefaultJWEDecrypterFactory();
082
083
084        @Override
085        public JWSKeySelector<C> getJWSKeySelector() {
086
087                return jwsKeySelector;
088        }
089
090
091        @Override
092        public void setJWSKeySelector(final JWSKeySelector<C> jwsKeySelector) {
093
094                this.jwsKeySelector = jwsKeySelector;
095        }
096
097
098        @Override
099        public JWEKeySelector<C> getJWEKeySelector() {
100
101                return jweKeySelector;
102        }
103
104
105        @Override
106        public void setJWEKeySelector(final JWEKeySelector<C> jweKeySelector) {
107
108                this.jweKeySelector = jweKeySelector;
109        }
110
111
112        @Override
113        public JWSVerifierFactory getJWSVerifierFactory() {
114
115                return jwsVerifierFactory;
116        }
117
118
119        @Override
120        public void setJWSVerifierFactory(final JWSVerifierFactory factory) {
121
122                jwsVerifierFactory = factory;
123        }
124
125
126        @Override
127        public JWEDecrypterFactory getJWEDecrypterFactory() {
128
129                return jweDecrypterFactory;
130        }
131
132
133        @Override
134        public void setJWEDecrypterFactory(final JWEDecrypterFactory factory) {
135
136                jweDecrypterFactory = factory;
137        }
138
139
140        @Override
141        public Payload process(final String compactJOSE, final C context)
142                throws ParseException, BadJOSEException, JOSEException {
143
144                return process(JOSEObject.parse(compactJOSE), context);
145        }
146
147
148        @Override
149        public Payload process(final JOSEObject joseObject, final C context)
150                throws BadJOSEException, JOSEException {
151
152                if (joseObject instanceof JWSObject) {
153                        return process((JWSObject)joseObject, context);
154                }
155
156                if (joseObject instanceof JWEObject) {
157                        return process((JWEObject)joseObject, context);
158                }
159
160                if (joseObject instanceof PlainObject) {
161                        return process((PlainObject)joseObject, context);
162                }
163
164                // Should never happen
165                throw new JOSEException("Unexpected JOSE object type: " + joseObject.getClass());
166        }
167
168
169        @Override
170        public Payload process(final PlainObject plainObject, C context)
171                throws BadJOSEException {
172
173                throw new BadJOSEException("Unsecured (plain) JOSE objects are rejected, extend class to handle");
174        }
175
176
177        @Override
178        public Payload process(final JWSObject jwsObject, C context)
179                throws BadJOSEException, JOSEException {
180
181                if (getJWSKeySelector() == null) {
182                        // JWS key selector may have been deliberately omitted
183                        throw new BadJOSEException("JWS object rejected: No JWS key selector is configured");
184                }
185
186                if (getJWSVerifierFactory() == null) {
187                        throw new JOSEException("No JWS verifier is configured");
188                }
189
190                List<? extends Key> keyCandidates = getJWSKeySelector().selectJWSKeys(jwsObject.getHeader(), context);
191
192                if (keyCandidates == null || keyCandidates.isEmpty()) {
193                        throw new BadJOSEException("JWS object rejected: No matching key(s) found");
194                }
195
196                ListIterator<? extends Key> it = keyCandidates.listIterator();
197
198                while (it.hasNext()) {
199
200                        JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(jwsObject.getHeader(), it.next());
201
202                        if (verifier == null) {
203                                continue;
204                        }
205
206                        final boolean validSignature = jwsObject.verify(verifier);
207
208                        if (validSignature) {
209                                return jwsObject.getPayload();
210                        }
211
212                        if (! it.hasNext()) {
213                                // No more keys to try out
214                                throw new BadJWSException("JWS object rejected: Invalid signature");
215                        }
216                }
217
218                throw new BadJOSEException("JWS object rejected: No matching verifier(s) found");
219        }
220
221
222        @Override
223        public Payload process(final JWEObject jweObject, C context)
224                throws BadJOSEException, JOSEException {
225
226                if (getJWEKeySelector() == null) {
227                        // JWE key selector may have been deliberately omitted
228                        throw new BadJOSEException("JWE object rejected: No JWE key selector is configured");
229                }
230
231                if (getJWEDecrypterFactory() == null) {
232                        throw new JOSEException("No JWE decrypter is configured");
233                }
234
235                List<? extends Key> keyCandidates = getJWEKeySelector().selectJWEKeys(jweObject.getHeader(), context);
236
237                if (keyCandidates == null || keyCandidates.isEmpty()) {
238                        throw new BadJOSEException("JWE object rejected: No matching key(s) found");
239                }
240
241                ListIterator<? extends Key> it = keyCandidates.listIterator();
242
243                while (it.hasNext()) {
244
245                        JWEDecrypter decrypter = getJWEDecrypterFactory().createJWEDecrypter(jweObject.getHeader(), it.next());
246
247                        if (decrypter == null) {
248                                continue;
249                        }
250
251                        try {
252                                jweObject.decrypt(decrypter);
253
254                        } catch (JOSEException e) {
255
256                                if (it.hasNext()) {
257                                        // Try next key
258                                        continue;
259                                }
260
261                                // No more keys to try
262                                throw new BadJWEException("JWE object rejected: " + e.getMessage(), e);
263                        }
264
265                        if ("JWT".equalsIgnoreCase(jweObject.getHeader().getContentType())) {
266
267                                // Handle nested signed JWT, see http://tools.ietf.org/html/rfc7519#section-5.2
268                                JWSObject nestedJWS = jweObject.getPayload().toJWSObject();
269
270                                if (nestedJWS == null) {
271                                        // Cannot parse payload to JWS object, return original form
272                                        return jweObject.getPayload();
273                                }
274
275                                return process(nestedJWS, context);
276                        }
277
278                        return jweObject.getPayload();
279                }
280
281                throw new BadJOSEException("JWE object rejected: No matching decrypter(s) found");
282        }
283}