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