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}