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}