001package com.nimbusds.jose.jwk;
002
003
004import java.util.Arrays;
005import java.util.Collections;
006import java.util.HashSet;
007import java.util.Set;
008
009import com.nimbusds.jose.Algorithm;
010import net.jcip.annotations.Immutable;
011
012
013/**
014 * JSON Web Key (JWK) matcher. May be used to ensure a JWK matches a set of
015 * application-specific criteria.
016 *
017 * <p>Supported key matching criteria:
018 *
019 * <ul>
020 *     <li>Any, unspecified, one or more key types (typ).
021 *     <li>Any, unspecified, one or more key uses (use).
022 *     <li>Any, unspecified, one or more key operations (key_ops).
023 *     <li>Any, unspecified, one or more key algorithms (alg).
024 *     <li>Any, unspecified, one or more key identifiers (kid).
025 *     <li>Private only key.
026 *     <li>Public only key.
027 * </ul>
028 *
029 * <p>Matching by X.509 certificate URL, thumbprint and chain is not supported.
030 *
031 * @author Vladimir Dzhuvinov
032 * @version 2016-07-03
033 */
034@Immutable
035public class JWKMatcher {
036
037
038        /**
039         * The key types to match.
040         */
041        private final Set<KeyType> types;
042
043
044        /**
045         * The public key uses to match.
046         */
047        private final Set<KeyUse> uses;
048
049
050        /**
051         * The key operations to match.
052         */
053        private final Set<KeyOperation> ops;
054
055
056        /**
057         * The algorithms to match.
058         */
059        private final Set<Algorithm> algs;
060
061
062        /**
063         * The key IDs to match.
064         */
065        private final Set<String> ids;
066
067
068        /**
069         * If {@code true} only private keys are matched.
070         */
071        private final boolean privateOnly;
072
073
074        /**
075         * If {@code true} only public keys are matched.
076         */
077        private final boolean publicOnly;
078
079
080        /**
081         * The minimum key size in bits, zero implies no minimum size limit.
082         */
083        private final int minSizeBits;
084
085
086        /**
087         * The maximum key size in bits, zero implies no maximum size limit.
088         */
089        private final int maxSizeBits;
090
091
092        /**
093         * Builder for constructing JWK matchers.
094         *
095         * <p>Example usage:
096         *
097         * <pre>
098         * JWKMatcher matcher = new JWKMatcher().keyID("123").build();
099         * </pre>
100         */
101        public static class Builder {
102
103                
104                /**
105                 * The key types to match.
106                 */
107                private Set<KeyType> types;
108
109
110                /**
111                 * The public key uses to match.
112                 */
113                private Set<KeyUse> uses;
114
115
116                /**
117                 * The key operations to match.
118                 */
119                private Set<KeyOperation> ops;
120
121
122                /**
123                 * The algorithms to match.
124                 */
125                private Set<Algorithm> algs;
126
127
128                /**
129                 * The key IDs to match.
130                 */
131                private Set<String> ids;
132
133
134                /**
135                 * If {@code true} only private keys are matched.
136                 */
137                private boolean privateOnly = false;
138
139
140                /**
141                 * If {@code true} only public keys are matched.
142                 */
143                private boolean publicOnly = false;
144
145
146                /**
147                 * The minimum key size in bits, zero implies no minimum size
148                 * limit.
149                 */
150                private int minSizeBits = 0;
151
152
153                /**
154                 * The maximum key size in bits, zero implies no maximum size
155                 * limit.
156                 */
157                private int maxSizeBits = 0;
158
159
160                /**
161                 * Sets a single key type to match.
162                 *
163                 * @param kty The key type, {@code null} if not specified.
164                 *            
165                 * @return This builder.            
166                 */
167                public Builder keyType(final KeyType kty) {
168
169                        if (kty == null) {
170                                types = null;
171                        } else {
172                                types = new HashSet<>(Collections.singletonList(kty));
173                        }
174                        
175                        return this;
176                }
177
178
179                /**
180                 * Sets multiple key types to match.
181                 *
182                 * @param types The key types.
183                 *
184                 * @return This builder.
185                 */
186                public Builder keyTypes(final KeyType ... types) {
187
188                        keyTypes(new HashSet<>(Arrays.asList(types)));
189                        return this;
190                }
191
192
193                /**
194                 * Sets multiple key types to match.
195                 *
196                 * @param types The key types, {@code null} if not specified.
197                 *
198                 * @return This builder.
199                 */
200                public Builder keyTypes(final Set<KeyType> types) {
201
202                        this.types = types;
203                        return this;
204                }
205
206
207                /**
208                 * Sets a single public key use to match.
209                 *
210                 * @param use The public key use, {@code null} if not 
211                 *            specified.
212                 *
213                 * @return This builder.
214                 */
215                public Builder keyUse(final KeyUse use) {
216
217                        if (use == null) {
218                                uses = null;
219                        } else {
220                                uses = new HashSet<>(Collections.singletonList(use));
221                        }
222                        return this;
223                }
224
225
226                /**
227                 * Sets multiple public key uses to match.
228                 *
229                 * @param uses The public key uses.
230                 *
231                 * @return This builder.
232                 */
233                public Builder keyUses(final KeyUse... uses) {
234
235                        keyUses(new HashSet<>(Arrays.asList(uses)));
236                        return this;
237                }
238
239
240                /**
241                 * Sets multiple public key uses to match.
242                 *
243                 * @param uses The public key uses, {@code null} if not
244                 *             specified.
245                 *
246                 * @return This builder.
247                 */
248                public Builder keyUses(final Set<KeyUse> uses) {
249
250                        this.uses = uses;
251                        return this;
252                }
253
254
255                /**
256                 * Sets a single key operation to match.
257                 *
258                 * @param op The key operation, {@code null} if not specified.
259                 *
260                 * @return This builder.
261                 */
262                public Builder keyOperation(final KeyOperation op) {
263
264                        if (op == null) {
265                                ops = null;
266                        } else {
267                                ops = new HashSet<>(Collections.singletonList(op));
268                        }
269                        return this;
270                }
271
272
273                /**
274                 * Sets multiple key operations to match.
275                 *
276                 * @param ops The key operations.
277                 *
278                 * @return This builder.
279                 */
280                public Builder keyOperations(final KeyOperation... ops) {
281
282                        keyOperations(new HashSet<>(Arrays.asList(ops)));
283                        return this;
284                }
285
286
287                /**
288                 * Sets multiple key operations to match.
289                 *
290                 * @param ops The key operations, {@code null} if not
291                 *            specified.
292                 *
293                 * @return This builder.
294                 */
295                public Builder keyOperations(final Set<KeyOperation> ops) {
296
297                        this.ops = ops;
298                        return this;
299                }
300
301
302                /**
303                 * Sets a single JOSE algorithm to match.
304                 *
305                 * @param alg The JOSE algorithm, {@code null} if not
306                 *            specified.
307                 *
308                 * @return This builder.
309                 */
310                public Builder algorithm(final Algorithm alg) {
311
312                        if (alg == null) {
313                                algs = null;
314                        } else {
315                                algs = new HashSet<>(Collections.singletonList(alg));
316                        }
317                        return this;
318                }
319
320
321                /**
322                 * Sets multiple JOSE algorithms to match.
323                 *
324                 * @param algs The JOSE algorithms.
325                 *
326                 * @return This builder.
327                 */
328                public Builder algorithms(final Algorithm ... algs) {
329
330                        algorithms(new HashSet<>(Arrays.asList(algs)));
331                        return this;
332                }
333
334
335                /**
336                 * Sets multiple JOSE algorithms to match.
337                 *
338                 * @param algs The JOSE algorithms, {@code null} if not
339                 *             specified.
340                 *
341                 * @return This builder.
342                 */
343                public Builder algorithms(final Set<Algorithm> algs) {
344
345                        this.algs = algs;
346                        return this;
347                }
348
349
350                /**
351                 * Sets a single key ID to match.
352                 *
353                 * @param id The key ID, {@code null} if not specified.
354                 *
355                 * @return This builder.
356                 */
357                public Builder keyID(final String id) {
358
359                        if (id == null) {
360                                ids = null;
361                        } else {
362                                ids = new HashSet<>(Collections.singletonList(id));
363                        }
364                        return this;
365                }
366
367
368                /**
369                 * Sets multiple key IDs to match.
370                 *
371                 * @param ids The key IDs.
372                 *
373                 * @return This builder.
374                 */
375                public Builder keyIDs(final String ... ids) {
376
377                        keyIDs(new HashSet<>(Arrays.asList(ids)));
378                        return this;
379                }
380
381
382                /**
383                 * Sets multiple key IDs to match.
384                 *
385                 * @param ids The key IDs, {@code null} if not specified.
386                 *
387                 * @return This builder.
388                 */
389                public Builder keyIDs(final Set<String> ids) {
390
391                        this.ids = ids;
392                        return this;
393                }
394
395
396                /**
397                 * Sets the private key matching policy.
398                 *
399                 * @param privateOnly If {@code true} only private keys are
400                 *                    matched.
401                 *
402                 * @return This builder.
403                 */
404                public Builder privateOnly(final boolean privateOnly) {
405
406                        this.privateOnly = privateOnly;
407                        return this;
408                }
409
410
411                /**
412                 * Sets the public key matching policy.
413                 *
414                 * @param publicOnly  If {@code true} only public keys are
415                 *                    matched.
416                 *
417                 * @return This builder.
418                 */
419                public Builder publicOnly(final boolean publicOnly) {
420
421                        this.publicOnly = publicOnly;
422                        return this;
423                }
424
425
426                /**
427                 * Sets the minimal key size.
428                 *
429                 * @param minSizeBits The minimum key size in bits, zero
430                 *                    implies no minimum key size limit.
431                 *
432                 * @return This builder.
433                 */
434                public Builder minKeySize(final int minSizeBits) {
435
436                        this.minSizeBits = minSizeBits;
437                        return this;
438                }
439
440
441                /**
442                 * Sets the maximum key size.
443                 *
444                 * @param maxSizeBits The maximum key size in bits, zero
445                 *                    implies no maximum key size limit.
446                 *
447                 * @return This builder.
448                 */
449                public Builder maxKeySize(final int maxSizeBits) {
450
451                        this.maxSizeBits = maxSizeBits;
452                        return this;
453                }
454
455
456                /**
457                 * Builds a new JWK matcher.
458                 *
459                 * @return The JWK matcher.
460                 */
461                public JWKMatcher build() {
462
463                        return new JWKMatcher(types, uses, ops, algs, ids, privateOnly, publicOnly, minSizeBits, maxSizeBits);
464                }
465        }
466
467
468        /**
469         * Creates a new JSON Web Key (JWK) matcher.
470         *
471         * @param types       The key types to match, {@code null} if not
472         *                    specified.
473         * @param uses        The public key uses to match, {@code null} if not
474         *                    specified.
475         * @param ops         The key operations to match, {@code null} if not
476         *                    specified.
477         * @param algs        The JOSE algorithms to match, {@code null} if not
478         *                    specified.
479         * @param ids         The key IDs to match, {@code null} if not
480         *                    specified.
481         * @param privateOnly If {@code true} only private keys are
482         *                    matched.
483         * @param publicOnly  If {@code true} only public keys are
484         *                    matched.
485         */
486        @Deprecated
487        public JWKMatcher(final Set<KeyType> types,
488                          final Set<KeyUse> uses,
489                          final Set<KeyOperation> ops,
490                          final Set<Algorithm> algs,
491                          final Set<String> ids,
492                          final boolean privateOnly,
493                          final boolean publicOnly) {
494
495                this(types, uses, ops, algs, ids, privateOnly, publicOnly, 0, 0);
496        }
497
498
499        /**
500         * Creates a new JSON Web Key (JWK) matcher.
501         *
502         * @param types       The key types to match, {@code null} if not
503         *                    specified.
504         * @param uses        The public key uses to match, {@code null} if not
505         *                    specified.
506         * @param ops         The key operations to match, {@code null} if not
507         *                    specified.
508         * @param algs        The JOSE algorithms to match, {@code null} if not
509         *                    specified.
510         * @param ids         The key IDs to match, {@code null} if not
511         *                    specified.
512         * @param privateOnly If {@code true} only private keys are
513         *                    matched.
514         * @param publicOnly  If {@code true} only public keys are
515         *                    matched.
516         * @param minSizeBits The minimum key size in bits, zero implies no
517         *                    minimum size limit.
518         * @param maxSizeBits The maximum key size in bits, zero implies no
519         *                    maximum size limit.
520         */
521        public JWKMatcher(final Set<KeyType> types,
522                          final Set<KeyUse> uses,
523                          final Set<KeyOperation> ops,
524                          final Set<Algorithm> algs,
525                          final Set<String> ids,
526                          final boolean privateOnly,
527                          final boolean publicOnly,
528                          final int minSizeBits,
529                          final int maxSizeBits) {
530
531                this.types = types;
532                this.uses = uses;
533                this.ops = ops;
534                this.algs = algs;
535                this.ids = ids;
536                this.privateOnly = privateOnly;
537                this.publicOnly = publicOnly;
538                this.minSizeBits = minSizeBits;
539                this.maxSizeBits = maxSizeBits;
540        }
541
542
543        /**
544         * Returns the key types to match.
545         *
546         * @return The key types, {@code null} if not specified.
547         */
548        public Set<KeyType> getKeyTypes() {
549
550                return types;
551        }
552
553
554        /**
555         * Returns the public key uses to match.
556         *
557         * @return The public key uses, {@code null} if not specified.
558         */
559        public Set<KeyUse> getKeyUses() {
560
561                return uses;
562        }
563
564
565        /**
566         * Returns the key operations to match.
567         *
568         * @return The key operations, {@code null} if not specified.
569         */
570        public Set<KeyOperation> getKeyOperations() {
571
572                return ops;
573        }
574
575
576        /**
577         * Returns the JOSE algorithms to match.
578         *
579         * @return The JOSE algorithms, {@code null} if not specified.
580         */
581        public Set<Algorithm> getAlgorithms() {
582
583                return algs;
584        }
585
586
587        /**
588         * Returns the key IDs to match.
589         *
590         * @return The key IDs, {@code null} if not specified.
591         */
592        public Set<String> getKeyIDs() {
593
594                return ids;
595        }
596
597
598        /**
599         * Returns {@code true} if only private keys are matched.
600         *
601         * @return {@code true} if only private keys are matched, else 
602         *         {@code false}.
603         */
604        public boolean isPrivateOnly() {
605
606                return privateOnly;
607        }
608
609
610        /**
611         * Returns {@code true} if only public keys are matched.
612         *
613         * @return {@code true} if only public keys are selected, else
614         *         {@code false}.
615         */
616        public boolean isPublicOnly() {
617
618                return publicOnly;
619        }
620
621
622        /**
623         * Returns the minimum key size.
624         *
625         * @return The minimum key size in bits, zero implies no minimum size
626         *         limit.
627         */
628        public int getMinSize() {
629
630                return minSizeBits;
631        }
632
633
634        /**
635         * Returns the maximum key size.
636         *
637         * @return The maximum key size in bits, zero implies no maximum size
638         *         limit.
639         */
640        public int getMaxSize() {
641
642                return maxSizeBits;
643        }
644
645
646        /**
647         * Returns {@code true} if the specified JWK matches.
648         *
649         * @param key The JSON Web Key (JWK). Must not  be {@code null}.
650         *
651         * @return {@code true} if the JWK matches, else {@code false}.
652         */
653        public boolean matches(final JWK key) {
654
655                if (privateOnly && ! key.isPrivate())
656                        return false;
657
658                if (publicOnly && key.isPrivate())
659                        return false;
660
661                if (types != null && ! types.contains(key.getKeyType()))
662                        return false;
663
664                if (uses != null && ! uses.contains(key.getKeyUse()))
665                        return false;
666
667                if (ops != null) {
668
669                        if (ops.contains(null) && key.getKeyOperations() == null) {
670                                // pass
671                        } else if (key.getKeyOperations() != null && ops.containsAll(key.getKeyOperations())) {
672                                // pass
673                        } else {
674                                return false;
675                        }
676                }
677
678                if (algs != null && ! algs.contains(key.getAlgorithm()))
679                        return false;
680
681                if (ids != null && ! ids.contains(key.getKeyID()))
682                        return false;
683
684                if (minSizeBits > 0) {
685
686                        if (key.size() < minSizeBits)
687                                return false;
688                }
689
690                if (maxSizeBits > 0) {
691
692                        if (key.size() > maxSizeBits)
693                                return false;
694                }
695
696                return true;
697        }
698}