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