001package com.nimbusds.jose.proc; 002 003 004import java.net.URI; 005import java.util.Arrays; 006import java.util.Collections; 007import java.util.HashSet; 008import java.util.Set; 009 010import com.nimbusds.jose.*; 011 012 013/** 014 * JOSE object / header matcher. May be used to ensure a JOSE object / header 015 * matches a set of application-specific criteria. 016 * 017 * <p>Supported matching criteria: 018 * 019 * <ul> 020 * <li>Any, one or more JOSE classes (plain, JWS, JWE). 021 * <li>Any, one or more algorithms (alg). 022 * <li>Any, one or more encryption methods (enc). 023 * <li>Any, one or more JWK URLs (jku). 024 * <li>Any, one or more JWK IDs (kid). 025 * </ul> 026 * 027 * <p>Matching by X.509 certificate URL, thumbprint and chain is not supported. 028 * 029 * @author Vladimir Dzhuvinov 030 * @version 2015-04-22 031 */ 032public class JOSEMatcher { 033 034 035 /** 036 * The JOSE classes to match. 037 */ 038 private final Set<Class<? extends JOSEObject>> classes; 039 040 041 /** 042 * The JOSE algorithms to match. 043 */ 044 private final Set<Algorithm> algs; 045 046 047 /** 048 * The JOSE encryption methods to match (applies to JWE only). 049 */ 050 private final Set<EncryptionMethod> encs; 051 052 053 /** 054 * The JWK URLs (jku) to match. 055 */ 056 private final Set<URI> jkus; 057 058 059 /** 060 * The JWK IDs (kid) to match. 061 */ 062 private final Set<String> kids; 063 064 065 /** 066 * Builder for constructing JOSE matchers. 067 * 068 * <p>Example usage: 069 * 070 * <pre> 071 * JOSEMatcher matcher = new JOSEMatcher().keyID("123").build(); 072 * </pre> 073 */ 074 public static class Builder { 075 076 077 /** 078 * The JOSE classes to match. 079 */ 080 private Set<Class<? extends JOSEObject>> classes; 081 082 083 /** 084 * The JOSE algorithms to match. 085 */ 086 private Set<Algorithm> algs; 087 088 089 /** 090 * The JOSE encryption methods to match (applies to JWE only). 091 */ 092 private Set<EncryptionMethod> encs; 093 094 095 /** 096 * The JWK URLs (jku) to match. 097 */ 098 private Set<URI> jkus; 099 100 101 /** 102 * The JWK IDs (kid) to match. 103 */ 104 private Set<String> kids; 105 106 107 /** 108 * Sets a single JOSE class to match. 109 * 110 * @param clazz The JOSE class to match, {@code null} if not 111 * specified. 112 * 113 * @return This builder. 114 */ 115 public Builder joseClass(final Class<? extends JOSEObject> clazz) { 116 117 if (clazz == null) { 118 this.classes = null; 119 } else { 120 this.classes = new HashSet<Class<? extends JOSEObject>>(Collections.singletonList(clazz)); 121 } 122 return this; 123 } 124 125 126 /** 127 * Sets multiple JOSE classes to match. 128 * 129 * @param classes The JOSE classes to match. 130 * 131 * @return This builder. 132 */ 133 public Builder joseClasses(final Class<? extends JOSEObject>... classes) { 134 135 joseClasses(new HashSet<>(Arrays.asList(classes))); 136 return this; 137 } 138 139 140 /** 141 * Sets multiple JOSE classes to match. 142 * 143 * @param classes The JOSE classes to match, {@code null} if 144 * not specified. 145 * 146 * @return This builder. 147 */ 148 public Builder joseClasses(final Set<Class<? extends JOSEObject>> classes) { 149 150 this.classes = classes; 151 return this; 152 } 153 154 155 /** 156 * Sets a single JOSE algorithm to match. 157 * 158 * @param alg The JOSE algorithm, {@code null} if not 159 * specified. 160 * 161 * @return This builder. 162 */ 163 public Builder algorithm(final Algorithm alg) { 164 165 if (alg == null) { 166 algs = null; 167 } else { 168 algs = new HashSet<>(Collections.singletonList(alg)); 169 } 170 return this; 171 } 172 173 174 /** 175 * Sets multiple JOSE algorithms to match. 176 * 177 * @param algs The JOSE algorithms. 178 * 179 * @return This builder. 180 */ 181 public Builder algorithms(final Algorithm ... algs) { 182 183 algorithms(new HashSet<>(Arrays.asList(algs))); 184 return this; 185 } 186 187 188 /** 189 * Sets multiple JOSE algorithms to match. 190 * 191 * @param algs The JOSE algorithms, {@code null} if not 192 * specified. 193 * 194 * @return This builder. 195 */ 196 public Builder algorithms(final Set<Algorithm> algs) { 197 198 this.algs = algs; 199 return this; 200 } 201 202 203 /** 204 * Sets a single JOSE encryption method to match. 205 * 206 * @param enc The JOSE encryption methods, {@code null} if not 207 * specified. 208 * 209 * @return This builder. 210 */ 211 public Builder encryptionMethod(final EncryptionMethod enc) { 212 213 if (enc == null) { 214 encs = null; 215 } else { 216 encs = new HashSet<>(Collections.singletonList(enc)); 217 } 218 return this; 219 } 220 221 222 /** 223 * Sets multiple JOSE encryption methods to match. 224 * 225 * @param encs The JOSE encryption methods. 226 * 227 * @return This builder. 228 */ 229 public Builder encryptionMethods(final EncryptionMethod... encs) { 230 231 encryptionMethods(new HashSet<>(Arrays.asList(encs))); 232 return this; 233 } 234 235 236 /** 237 * Sets multiple JOSE encryption methods to match. 238 * 239 * @param encs The JOSE encryption methods, {@code null} if not 240 * specified. 241 * 242 * @return This builder. 243 */ 244 public Builder encryptionMethods(final Set<EncryptionMethod> encs) { 245 246 this.encs = encs; 247 return this; 248 } 249 250 251 /** 252 * Sets a single JWK URL to match. 253 * 254 * @param jku The JWK URL, {@code null} if not specified. 255 * 256 * @return This builder. 257 */ 258 public Builder jwkURL(final URI jku) { 259 260 if (jku == null) { 261 jkus = null; 262 } else { 263 jkus = new HashSet<>(Collections.singletonList(jku)); 264 } 265 return this; 266 } 267 268 269 /** 270 * Sets multiple JWK URLs to match. 271 * 272 * @param jkus The JWK URLs. 273 * 274 * @return This builder. 275 */ 276 public Builder jwkURLs(final URI... jkus) { 277 278 jwkURLs(new HashSet<>(Arrays.asList(jkus))); 279 return this; 280 } 281 282 283 /** 284 * Sets multiple JWK URLs to match. 285 * 286 * @param jkus The JWK URLs, {@code null} if not specified. 287 * 288 * @return This builder. 289 */ 290 public Builder jwkURLs(final Set<URI> jkus) { 291 292 this.jkus = jkus; 293 return this; 294 } 295 296 297 /** 298 * Sets a single key ID to match. 299 * 300 * @param kid The key ID, {@code null} if not specified. 301 * 302 * @return This builder. 303 */ 304 public Builder keyID(final String kid) { 305 306 if (kid == null) { 307 kids = null; 308 } else { 309 kids = new HashSet<>(Collections.singletonList(kid)); 310 } 311 return this; 312 } 313 314 315 /** 316 * Sets multiple key IDs to match. 317 * 318 * @param ids The key IDs. 319 * 320 * @return This builder. 321 */ 322 public Builder keyIDs(final String ... ids) { 323 324 keyIDs(new HashSet<>(Arrays.asList(ids))); 325 return this; 326 } 327 328 329 /** 330 * Sets multiple key IDs to match. 331 * 332 * @param kids The key IDs, {@code null} if not specified. 333 * 334 * @return This builder. 335 */ 336 public Builder keyIDs(final Set<String> kids) { 337 338 this.kids = kids; 339 return this; 340 } 341 342 343 /** 344 * Builds a new JOSE matcher. 345 * 346 * @return The JOSE matcher. 347 */ 348 public JOSEMatcher build() { 349 350 return new JOSEMatcher(classes, algs, encs, jkus, kids); 351 } 352 } 353 354 355 /** 356 * Creates a new JOSE matcher. 357 * 358 * @param classes The JOSE classes to match, {@code null} if not 359 * specified. 360 * @param algs The JOSE algorithms to match, {@code null} if not 361 * specified. 362 * @param encs The JOSE encryption methods to match, {@code null} if 363 * not specified. 364 * @param jkus The JWK URLs to match, {@code null} if not specified. 365 * @param kids The key IDs to match, {@code null} if not specified. 366 */ 367 public JOSEMatcher(final Set<Class<? extends JOSEObject>> classes, 368 final Set<Algorithm> algs, 369 final Set<EncryptionMethod> encs, 370 final Set<URI> jkus, 371 final Set<String> kids) { 372 373 this.classes = classes; 374 this.algs = algs; 375 this.encs = encs; 376 this.jkus = jkus; 377 this.kids = kids; 378 } 379 380 381 /** 382 * Returns the JOSE classes to match. 383 * 384 * @return The JOSE classes, {@code null} if not specified. 385 */ 386 public Set<Class<? extends JOSEObject>> getJOSEClasses() { 387 388 return classes; 389 } 390 391 392 /** 393 * Returns the JOSE algorithms to match. 394 * 395 * @return The JOSE algorithms, {@code null} if not specified. 396 */ 397 public Set<Algorithm> getAlgorithms() { 398 399 return algs; 400 } 401 402 403 /** 404 * Returns the JOSE encryption methods to match. 405 * 406 * @return The JOSE encryption methods, {@code null} if not specified. 407 */ 408 public Set<EncryptionMethod> getEncryptionMethods() { 409 410 return encs; 411 } 412 413 414 /** 415 * Returns the JWK URLs to match. 416 * 417 * @return The JWK URLs, {@code null} if not specified. 418 */ 419 public Set<URI> getJWKURLs() { 420 421 return jkus; 422 } 423 424 425 /** 426 * Returns the key IDs to match. 427 * 428 * @return The key IDs, {@code null} if not specified. 429 */ 430 public Set<String> getKeyIDs() { 431 432 return kids; 433 } 434 435 436 /** 437 * Returns {@code true} if the specified JOSE object matches. 438 * 439 * @param joseObject The JOSE object. Must not be {@code null}. 440 * 441 * @return {@code true} if the JOSE object matches, else {@code false}. 442 */ 443 public boolean matches(final JOSEObject joseObject) { 444 445 if (classes != null) { 446 447 boolean pass = false; 448 for (Class<? extends JOSEObject> c: classes) { 449 if (c != null && c.isInstance(joseObject)) { 450 pass = true; 451 } 452 } 453 454 if (!pass) { 455 return false; 456 } 457 } 458 459 if (algs != null && ! algs.contains(joseObject.getHeader().getAlgorithm())) 460 return false; 461 462 if (encs != null) { 463 464 if (! (joseObject instanceof JWEObject)) 465 return false; 466 467 JWEObject jweObject = (JWEObject)joseObject; 468 469 if (! encs.contains(jweObject.getHeader().getEncryptionMethod())) 470 return false; 471 } 472 473 if (jkus != null) { 474 475 final URI jku; 476 477 if (joseObject instanceof JWSObject) { 478 jku = ((JWSObject) joseObject).getHeader().getJWKURL(); 479 } else if (joseObject instanceof JWEObject) { 480 jku = ((JWEObject) joseObject).getHeader().getJWKURL(); 481 } else { 482 // Plain object 483 jku = null; // jku not supported by unsecured JOSE objects 484 } 485 486 if (! jkus.contains(jku)) 487 return false; 488 } 489 490 if (kids != null) { 491 492 final String kid; 493 494 if (joseObject instanceof JWSObject) { 495 kid = ((JWSObject) joseObject).getHeader().getKeyID(); 496 } else if (joseObject instanceof JWEObject) { 497 kid = ((JWEObject) joseObject).getHeader().getKeyID(); 498 } else { 499 // Plain object 500 kid = null; // kid not supported by unsecured JOSE objects 501 } 502 503 if (! kids.contains(kid)) 504 return false; 505 } 506 507 return true; 508 } 509}