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.net.URI;
022import java.util.Arrays;
023import java.util.Collections;
024import java.util.HashSet;
025import java.util.Set;
026
027import com.nimbusds.jose.*;
028
029
030/**
031 * JOSE object / header matcher. May be used to ensure a JOSE object / header
032 * matches a set of application-specific criteria.
033 *
034 * <p>Supported matching criteria:
035 *
036 * <ul>
037 *     <li>Any, one or more JOSE classes (plain, JWS, JWE).
038 *     <li>Any, one or more algorithms (alg).
039 *     <li>Any, one or more encryption methods (enc).
040 *     <li>Any, one or more JWK URLs (jku).
041 *     <li>Any, one or more JWK IDs (kid).
042 * </ul>
043 *
044 * <p>Matching by X.509 certificate URL, thumbprint and chain is not supported.
045 *
046 * @author Vladimir Dzhuvinov
047 * @version 2015-04-22
048 */
049public class JOSEMatcher {
050
051
052        /**
053         * The JOSE classes to match.
054         */
055        private final Set<Class<? extends JOSEObject>> classes;
056
057
058        /**
059         * The JOSE algorithms to match.
060         */
061        private final Set<Algorithm> algs;
062
063
064        /**
065         * The JOSE encryption methods to match (applies to JWE only).
066         */
067        private final Set<EncryptionMethod> encs;
068
069
070        /**
071         * The JWK URLs (jku) to match.
072         */
073        private final Set<URI> jkus;
074
075
076        /**
077         * The JWK IDs (kid) to match.
078         */
079        private final Set<String> kids;
080
081
082        /**
083         * Builder for constructing JOSE matchers.
084         *
085         * <p>Example usage:
086         *
087         * <pre>
088         * JOSEMatcher matcher = new JOSEMatcher().keyID("123").build();
089         * </pre>
090         */
091        public static class Builder {
092
093
094                /**
095                 * The JOSE classes to match.
096                 */
097                private Set<Class<? extends JOSEObject>> classes;
098
099
100                /**
101                 * The JOSE algorithms to match.
102                 */
103                private Set<Algorithm> algs;
104
105
106                /**
107                 * The JOSE encryption methods to match (applies to JWE only).
108                 */
109                private Set<EncryptionMethod> encs;
110
111
112                /**
113                 * The JWK URLs (jku) to match.
114                 */
115                private Set<URI> jkus;
116
117
118                /**
119                 * The JWK IDs (kid) to match.
120                 */
121                private Set<String> kids;
122
123
124                /**
125                 * Sets a single JOSE class to match.
126                 *
127                 * @param clazz The JOSE class to match, {@code null} if not
128                 *              specified.
129                 *
130                 * @return This builder.
131                 */
132                public Builder joseClass(final Class<? extends JOSEObject> clazz) {
133
134                        if (clazz == null) {
135                                this.classes = null;
136                        } else {
137                                this.classes = new HashSet<Class<? extends JOSEObject>>(Collections.singletonList(clazz));
138                        }
139                        return this;
140                }
141
142
143                /**
144                 * Sets multiple JOSE classes to match.
145                 *
146                 * @param classes The JOSE classes to match.
147                 *
148                 * @return This builder.
149                 */
150                public Builder joseClasses(final Class<? extends JOSEObject>... classes) {
151
152                        joseClasses(new HashSet<>(Arrays.asList(classes)));
153                        return this;
154                }
155
156
157                /**
158                 * Sets multiple JOSE classes to match.
159                 *
160                 * @param classes The JOSE classes to match, {@code null} if
161                 *                not specified.
162                 *
163                 * @return This builder.
164                 */
165                public Builder joseClasses(final Set<Class<? extends JOSEObject>> classes) {
166
167                        this.classes = classes;
168                        return this;
169                }
170
171
172                /**
173                 * Sets a single JOSE algorithm to match.
174                 *
175                 * @param alg The JOSE algorithm, {@code null} if not
176                 *            specified.
177                 *
178                 * @return This builder.
179                 */
180                public Builder algorithm(final Algorithm alg) {
181
182                        if (alg == null) {
183                                algs = null;
184                        } else {
185                                algs = new HashSet<>(Collections.singletonList(alg));
186                        }
187                        return this;
188                }
189
190
191                /**
192                 * Sets multiple JOSE algorithms to match.
193                 *
194                 * @param algs The JOSE algorithms.
195                 *
196                 * @return This builder.
197                 */
198                public Builder algorithms(final Algorithm ... algs) {
199
200                        algorithms(new HashSet<>(Arrays.asList(algs)));
201                        return this;
202                }
203
204
205                /**
206                 * Sets multiple JOSE algorithms to match.
207                 *
208                 * @param algs The JOSE algorithms, {@code null} if not
209                 *             specified.
210                 *
211                 * @return This builder.
212                 */
213                public Builder algorithms(final Set<Algorithm> algs) {
214
215                        this.algs = algs;
216                        return this;
217                }
218
219
220                /**
221                 * Sets a single JOSE encryption method to match.
222                 *
223                 * @param enc The JOSE encryption methods, {@code null} if not
224                 *            specified.
225                 *
226                 * @return This builder.
227                 */
228                public Builder encryptionMethod(final EncryptionMethod enc) {
229
230                        if (enc == null) {
231                                encs = null;
232                        } else {
233                                encs = new HashSet<>(Collections.singletonList(enc));
234                        }
235                        return this;
236                }
237
238
239                /**
240                 * Sets multiple JOSE encryption methods to match.
241                 *
242                 * @param encs The JOSE encryption methods.
243                 *
244                 * @return This builder.
245                 */
246                public Builder encryptionMethods(final EncryptionMethod... encs) {
247
248                        encryptionMethods(new HashSet<>(Arrays.asList(encs)));
249                        return this;
250                }
251
252
253                /**
254                 * Sets multiple JOSE encryption methods to match.
255                 *
256                 * @param encs The JOSE encryption methods, {@code null} if not
257                 *             specified.
258                 *
259                 * @return This builder.
260                 */
261                public Builder encryptionMethods(final Set<EncryptionMethod> encs) {
262
263                        this.encs = encs;
264                        return this;
265                }
266
267
268                /**
269                 * Sets a single JWK URL to match.
270                 *
271                 * @param jku The JWK URL, {@code null} if not specified.
272                 *
273                 * @return This builder.
274                 */
275                public Builder jwkURL(final URI jku) {
276
277                        if (jku == null) {
278                                jkus = null;
279                        } else {
280                                jkus = new HashSet<>(Collections.singletonList(jku));
281                        }
282                        return this;
283                }
284
285
286                /**
287                 * Sets multiple JWK URLs to match.
288                 *
289                 * @param jkus The JWK URLs.
290                 *
291                 * @return This builder.
292                 */
293                public Builder jwkURLs(final URI... jkus) {
294
295                        jwkURLs(new HashSet<>(Arrays.asList(jkus)));
296                        return this;
297                }
298
299
300                /**
301                 * Sets multiple JWK URLs to match.
302                 *
303                 * @param jkus The JWK URLs, {@code null} if not specified.
304                 *
305                 * @return This builder.
306                 */
307                public Builder jwkURLs(final Set<URI> jkus) {
308
309                        this.jkus = jkus;
310                        return this;
311                }
312
313
314                /**
315                 * Sets a single key ID to match.
316                 *
317                 * @param kid The key ID, {@code null} if not specified.
318                 *
319                 * @return This builder.
320                 */
321                public Builder keyID(final String kid) {
322
323                        if (kid == null) {
324                                kids = null;
325                        } else {
326                                kids = new HashSet<>(Collections.singletonList(kid));
327                        }
328                        return this;
329                }
330
331
332                /**
333                 * Sets multiple key IDs to match.
334                 *
335                 * @param ids The key IDs.
336                 *
337                 * @return This builder.
338                 */
339                public Builder keyIDs(final String ... ids) {
340
341                        keyIDs(new HashSet<>(Arrays.asList(ids)));
342                        return this;
343                }
344
345
346                /**
347                 * Sets multiple key IDs to match.
348                 *
349                 * @param kids The key IDs, {@code null} if not specified.
350                 *
351                 * @return This builder.
352                 */
353                public Builder keyIDs(final Set<String> kids) {
354
355                        this.kids = kids;
356                        return this;
357                }
358
359
360                /**
361                 * Builds a new JOSE matcher.
362                 *
363                 * @return The JOSE matcher.
364                 */
365                public JOSEMatcher build() {
366
367                        return new JOSEMatcher(classes, algs, encs, jkus, kids);
368                }
369        }
370
371
372        /**
373         * Creates a new JOSE matcher.
374         *
375         * @param classes The JOSE classes to match, {@code null} if not
376         *                specified.
377         * @param algs    The JOSE algorithms to match, {@code null} if not
378         *                specified.
379         * @param encs    The JOSE encryption methods to match, {@code null} if
380         *                not specified.
381         * @param jkus    The JWK URLs to match, {@code null} if not specified.
382         * @param kids    The key IDs to match, {@code null} if not specified.
383         */
384        public JOSEMatcher(final Set<Class<? extends JOSEObject>> classes,
385                           final Set<Algorithm> algs,
386                           final Set<EncryptionMethod> encs,
387                           final Set<URI> jkus,
388                           final Set<String> kids) {
389
390                this.classes = classes;
391                this.algs = algs;
392                this.encs = encs;
393                this.jkus = jkus;
394                this.kids = kids;
395        }
396
397
398        /**
399         * Returns the JOSE classes to match.
400         *
401         * @return The JOSE classes, {@code null} if not specified.
402         */
403        public Set<Class<? extends JOSEObject>> getJOSEClasses() {
404
405                return classes;
406        }
407
408
409        /**
410         * Returns the JOSE algorithms to match.
411         *
412         * @return The JOSE algorithms, {@code null} if not specified.
413         */
414        public Set<Algorithm> getAlgorithms() {
415
416                return algs;
417        }
418
419
420        /**
421         * Returns the JOSE encryption methods to match.
422         *
423         * @return The JOSE encryption methods, {@code null} if not specified.
424         */
425        public Set<EncryptionMethod> getEncryptionMethods() {
426
427                return encs;
428        }
429
430
431        /**
432         * Returns the JWK URLs to match.
433         *
434         * @return The JWK URLs, {@code null} if not specified.
435         */
436        public Set<URI> getJWKURLs() {
437
438                return jkus;
439        }
440
441
442        /**
443         * Returns the key IDs to match.
444         *
445         * @return The key IDs, {@code null} if not specified.
446         */
447        public Set<String> getKeyIDs() {
448
449                return kids;
450        }
451
452
453        /**
454         * Returns {@code true} if the specified JOSE object matches.
455         *
456         * @param joseObject The JOSE object. Must not  be {@code null}.
457         *
458         * @return {@code true} if the JOSE object matches, else {@code false}.
459         */
460        public boolean matches(final JOSEObject joseObject) {
461
462                if (classes != null) {
463
464                        boolean pass = false;
465                        for (Class<? extends JOSEObject> c: classes) {
466                                if (c != null && c.isInstance(joseObject)) {
467                                        pass = true;
468                                }
469                        }
470
471                        if (!pass) {
472                                return false;
473                        }
474                }
475
476                if (algs != null && ! algs.contains(joseObject.getHeader().getAlgorithm()))
477                        return false;
478
479                if (encs != null) {
480
481                        if (! (joseObject instanceof JWEObject))
482                                return false;
483
484                        JWEObject jweObject = (JWEObject)joseObject;
485
486                        if (! encs.contains(jweObject.getHeader().getEncryptionMethod()))
487                                return false;
488                }
489
490                if (jkus != null) {
491
492                        final URI jku;
493
494                        if (joseObject instanceof JWSObject) {
495                                jku = ((JWSObject) joseObject).getHeader().getJWKURL();
496                        } else if (joseObject instanceof JWEObject) {
497                                jku = ((JWEObject) joseObject).getHeader().getJWKURL();
498                        } else {
499                                // Plain object
500                                jku = null; // jku not supported by unsecured JOSE objects
501                        }
502
503                        if (! jkus.contains(jku))
504                                return false;
505                }
506
507                if (kids != null) {
508
509                        final String kid;
510
511                        if (joseObject instanceof JWSObject) {
512                                kid = ((JWSObject) joseObject).getHeader().getKeyID();
513                        } else if (joseObject instanceof JWEObject) {
514                                kid = ((JWEObject) joseObject).getHeader().getKeyID();
515                        } else {
516                                // Plain object
517                                kid = null; // kid not supported by unsecured JOSE objects
518                        }
519
520                        if (! kids.contains(kid))
521                                return false;
522                }
523
524                return true;
525        }
526}