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}