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}