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