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