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