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; 019 020 021import java.net.URI; 022import java.text.ParseException; 023import java.util.*; 024 025import com.nimbusds.jose.jwk.JWK; 026import com.nimbusds.jose.util.Base64; 027import com.nimbusds.jose.util.Base64URL; 028import com.nimbusds.jose.util.JSONObjectUtils; 029import com.nimbusds.jose.util.X509CertChainUtils; 030import net.jcip.annotations.Immutable; 031import net.minidev.json.JSONObject; 032 033 034/** 035 * JSON Web Signature (JWS) header. This class is immutable. 036 * 037 * <p>Supports all {@link #getRegisteredParameterNames registered header 038 * parameters} of the JWS specification (RFC 7515) and the "b64" header from 039 * JWS Unencoded Payload Option (RFC 7797): 040 * 041 * <ul> 042 * <li>alg 043 * <li>jku 044 * <li>jwk 045 * <li>x5u 046 * <li>x5t 047 * <li>x5t#S256 048 * <li>x5c 049 * <li>kid 050 * <li>typ 051 * <li>cty 052 * <li>crit 053 * <li>b64 054 * </ul> 055 * 056 * <p>The header may also include {@link #getCustomParams custom 057 * parameters}; these will be serialised and parsed along the registered ones. 058 * 059 * <p>Example header of a JSON Web Signature (JWS) object using the 060 * {@link JWSAlgorithm#HS256 HMAC SHA-256 algorithm}: 061 * 062 * <pre> 063 * { 064 * "alg" : "HS256" 065 * } 066 * </pre> 067 * 068 * @author Vladimir Dzhuvinov 069 * @version 2020-06-02 070 */ 071@Immutable 072public final class JWSHeader extends CommonSEHeader { 073 074 075 private static final long serialVersionUID = 1L; 076 077 078 /** 079 * The registered parameter names. 080 */ 081 private static final Set<String> REGISTERED_PARAMETER_NAMES; 082 083 084 static { 085 Set<String> p = new HashSet<>(); 086 087 p.add("alg"); 088 p.add("jku"); 089 p.add("jwk"); 090 p.add("x5u"); 091 p.add("x5t"); 092 p.add("x5t#S256"); 093 p.add("x5c"); 094 p.add("kid"); 095 p.add("typ"); 096 p.add("cty"); 097 p.add("crit"); 098 p.add("b64"); 099 100 REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p); 101 } 102 103 104 /** 105 * Builder for constructing JSON Web Signature (JWS) headers. 106 * 107 * <p>Example usage: 108 * 109 * <pre> 110 * JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS256). 111 * contentType("text/plain"). 112 * customParam("exp", new Date().getTime()). 113 * build(); 114 * </pre> 115 */ 116 public static class Builder { 117 118 119 /** 120 * The JWS algorithm. 121 */ 122 private final JWSAlgorithm alg; 123 124 125 /** 126 * The JOSE object type. 127 */ 128 private JOSEObjectType typ; 129 130 131 /** 132 * The content type. 133 */ 134 private String cty; 135 136 137 /** 138 * The critical headers. 139 */ 140 private Set<String> crit; 141 142 143 /** 144 * JWK Set URL. 145 */ 146 private URI jku; 147 148 149 /** 150 * JWK. 151 */ 152 private JWK jwk; 153 154 155 /** 156 * X.509 certificate URL. 157 */ 158 private URI x5u; 159 160 161 /** 162 * X.509 certificate SHA-1 thumbprint. 163 */ 164 @Deprecated 165 private Base64URL x5t; 166 167 168 /** 169 * X.509 certificate SHA-256 thumbprint. 170 */ 171 private Base64URL x5t256; 172 173 174 /** 175 * The X.509 certificate chain corresponding to the key used to 176 * sign the JWS object. 177 */ 178 private List<Base64> x5c; 179 180 181 /** 182 * Key ID. 183 */ 184 private String kid; 185 186 187 /** 188 * Base64URL encoding of the payload, the default is 189 * {@code true} for standard JWS serialisation. 190 */ 191 private boolean b64 = true; 192 193 194 /** 195 * Custom header parameters. 196 */ 197 private Map<String,Object> customParams; 198 199 200 /** 201 * The parsed Base64URL. 202 */ 203 private Base64URL parsedBase64URL; 204 205 206 /** 207 * Creates a new JWS header builder. 208 * 209 * @param alg The JWS algorithm ({@code alg}) parameter. Must 210 * not be "none" or {@code null}. 211 */ 212 public Builder(final JWSAlgorithm alg) { 213 214 if (alg.getName().equals(Algorithm.NONE.getName())) { 215 throw new IllegalArgumentException("The JWS algorithm \"alg\" cannot be \"none\""); 216 } 217 218 this.alg = alg; 219 } 220 221 222 /** 223 * Creates a new JWS header builder with the parameters from 224 * the specified header. 225 * 226 * @param jwsHeader The JWS header to use. Must not not be 227 * {@code null}. 228 */ 229 public Builder(final JWSHeader jwsHeader) { 230 231 this(jwsHeader.getAlgorithm()); 232 233 typ = jwsHeader.getType(); 234 cty = jwsHeader.getContentType(); 235 crit = jwsHeader.getCriticalParams(); 236 237 jku = jwsHeader.getJWKURL(); 238 jwk = jwsHeader.getJWK(); 239 x5u = jwsHeader.getX509CertURL(); 240 x5t = jwsHeader.getX509CertThumbprint(); 241 x5t256 = jwsHeader.getX509CertSHA256Thumbprint(); 242 x5c = jwsHeader.getX509CertChain(); 243 kid = jwsHeader.getKeyID(); 244 b64 = jwsHeader.isBase64URLEncodePayload(); 245 customParams = jwsHeader.getCustomParams(); 246 } 247 248 249 /** 250 * Sets the type ({@code typ}) parameter. 251 * 252 * @param typ The type parameter, {@code null} if not 253 * specified. 254 * 255 * @return This builder. 256 */ 257 public Builder type(final JOSEObjectType typ) { 258 259 this.typ = typ; 260 return this; 261 } 262 263 264 /** 265 * Sets the content type ({@code cty}) parameter. 266 * 267 * @param cty The content type parameter, {@code null} if not 268 * specified. 269 * 270 * @return This builder. 271 */ 272 public Builder contentType(final String cty) { 273 274 this.cty = cty; 275 return this; 276 } 277 278 279 /** 280 * Sets the critical header parameters ({@code crit}) 281 * parameter. 282 * 283 * @param crit The names of the critical header parameters, 284 * empty set or {@code null} if none. 285 * 286 * @return This builder. 287 */ 288 public Builder criticalParams(final Set<String> crit) { 289 290 this.crit = crit; 291 return this; 292 } 293 294 295 /** 296 * Sets the JSON Web Key (JWK) Set URL ({@code jku}) parameter. 297 * 298 * @param jku The JSON Web Key (JWK) Set URL parameter, 299 * {@code null} if not specified. 300 * 301 * @return This builder. 302 */ 303 public Builder jwkURL(final URI jku) { 304 305 this.jku = jku; 306 return this; 307 } 308 309 310 /** 311 * Sets the JSON Web Key (JWK) ({@code jwk}) parameter. 312 * 313 * @param jwk The JSON Web Key (JWK) ({@code jwk}) parameter, 314 * {@code null} if not specified. 315 * 316 * @return This builder. 317 */ 318 public Builder jwk(final JWK jwk) { 319 320 this.jwk = jwk; 321 return this; 322 } 323 324 325 /** 326 * Sets the X.509 certificate URL ({@code x5u}) parameter. 327 * 328 * @param x5u The X.509 certificate URL parameter, {@code null} 329 * if not specified. 330 * 331 * @return This builder. 332 */ 333 public Builder x509CertURL(final URI x5u) { 334 335 this.x5u = x5u; 336 return this; 337 } 338 339 340 /** 341 * Sets the X.509 certificate SHA-1 thumbprint ({@code x5t}) 342 * parameter. 343 * 344 * @param x5t The X.509 certificate SHA-1 thumbprint parameter, 345 * {@code null} if not specified. 346 * 347 * @return This builder. 348 */ 349 @Deprecated 350 public Builder x509CertThumbprint(final Base64URL x5t) { 351 352 this.x5t = x5t; 353 return this; 354 } 355 356 357 /** 358 * Sets the X.509 certificate SHA-256 thumbprint 359 * ({@code x5t#S256}) parameter. 360 * 361 * @param x5t256 The X.509 certificate SHA-256 thumbprint 362 * parameter, {@code null} if not specified. 363 * 364 * @return This builder. 365 */ 366 public Builder x509CertSHA256Thumbprint(final Base64URL x5t256) { 367 368 this.x5t256 = x5t256; 369 return this; 370 } 371 372 373 /** 374 * Sets the X.509 certificate chain parameter ({@code x5c}) 375 * corresponding to the key used to sign the JWS object. 376 * 377 * @param x5c The X.509 certificate chain parameter, 378 * {@code null} if not specified. 379 * 380 * @return This builder. 381 */ 382 public Builder x509CertChain(final List<Base64> x5c) { 383 384 this.x5c = x5c; 385 return this; 386 } 387 388 389 /** 390 * Sets the key ID ({@code kid}) parameter. 391 * 392 * @param kid The key ID parameter, {@code null} if not 393 * specified. 394 * 395 * @return This builder. 396 */ 397 public Builder keyID(final String kid) { 398 399 this.kid = kid; 400 return this; 401 } 402 403 404 /** 405 * Sets the Base64URL encode payload ({@code b64}) parameter. 406 * 407 * @param b64 {@code true} to Base64URL encode the payload 408 * for standard JWS serialisation, {@code false} for 409 * unencoded payload (RFC 7797). 410 * 411 * @return This builder. 412 */ 413 public Builder base64URLEncodePayload(final boolean b64) { 414 415 this.b64 = b64; 416 return this; 417 } 418 419 420 /** 421 * Sets a custom (non-registered) parameter. 422 * 423 * @param name The name of the custom parameter. Must not 424 * match a registered parameter name and must not 425 * be {@code null}. 426 * @param value The value of the custom parameter, should map 427 * to a valid JSON entity, {@code null} if not 428 * specified. 429 * 430 * @return This builder. 431 * 432 * @throws IllegalArgumentException If the specified parameter 433 * name matches a registered 434 * parameter name. 435 */ 436 public Builder customParam(final String name, final Object value) { 437 438 if (getRegisteredParameterNames().contains(name)) { 439 throw new IllegalArgumentException("The parameter name \"" + name + "\" matches a registered name"); 440 } 441 442 if (customParams == null) { 443 customParams = new HashMap<>(); 444 } 445 446 customParams.put(name, value); 447 448 return this; 449 } 450 451 452 /** 453 * Sets the custom (non-registered) parameters. The values must 454 * be serialisable to a JSON entity, otherwise will be ignored. 455 * 456 * @param customParameters The custom parameters, empty map or 457 * {@code null} if none. 458 * 459 * @return This builder. 460 */ 461 public Builder customParams(final Map<String, Object> customParameters) { 462 463 this.customParams = customParameters; 464 return this; 465 } 466 467 468 /** 469 * Sets the parsed Base64URL. 470 * 471 * @param base64URL The parsed Base64URL, {@code null} if the 472 * header is created from scratch. 473 * 474 * @return This builder. 475 */ 476 public Builder parsedBase64URL(final Base64URL base64URL) { 477 478 this.parsedBase64URL = base64URL; 479 return this; 480 } 481 482 483 /** 484 * Builds a new JWS header. 485 * 486 * @return The JWS header. 487 */ 488 public JWSHeader build() { 489 490 return new JWSHeader( 491 alg, typ, cty, crit, 492 jku, jwk, x5u, x5t, x5t256, x5c, kid, b64, 493 customParams, parsedBase64URL); 494 } 495 } 496 497 498 /** 499 * Base64URL encoding of the payload, {@code true} for standard JWS 500 * serialisation, {@code false} for unencoded payload (RFC 7797). 501 */ 502 private final boolean b64; 503 504 505 /** 506 * Creates a new minimal JSON Web Signature (JWS) header. 507 * 508 * <p>Note: Use {@link PlainHeader} to create a header with algorithm 509 * {@link Algorithm#NONE none}. 510 * 511 * @param alg The JWS algorithm ({@code alg}) parameter. Must not be 512 * "none" or {@code null}. 513 */ 514 public JWSHeader(final JWSAlgorithm alg) { 515 516 this(alg, null, null, null, null, null, null, null, null, null, null, true,null, null); 517 } 518 519 520 /** 521 * Creates a new JSON Web Signature (JWS) header. 522 * 523 * <p>Note: Use {@link PlainHeader} to create a header with algorithm 524 * {@link Algorithm#NONE none}. 525 * 526 * @param alg The JWS algorithm ({@code alg}) parameter. 527 * Must not be "none" or {@code null}. 528 * @param typ The type ({@code typ}) parameter, 529 * {@code null} if not specified. 530 * @param cty The content type ({@code cty}) parameter, 531 * {@code null} if not specified. 532 * @param crit The names of the critical header 533 * ({@code crit}) parameters, empty set or 534 * {@code null} if none. 535 * @param jku The JSON Web Key (JWK) Set URL ({@code jku}) 536 * parameter, {@code null} if not specified. 537 * @param jwk The X.509 certificate URL ({@code jwk}) 538 * parameter, {@code null} if not specified. 539 * @param x5u The X.509 certificate URL parameter 540 * ({@code x5u}), {@code null} if not specified. 541 * @param x5t The X.509 certificate SHA-1 thumbprint 542 * ({@code x5t}) parameter, {@code null} if not 543 * specified. 544 * @param x5t256 The X.509 certificate SHA-256 thumbprint 545 * ({@code x5t#S256}) parameter, {@code null} if 546 * not specified. 547 * @param x5c The X.509 certificate chain ({@code x5c}) 548 * parameter, {@code null} if not specified. 549 * @param kid The key ID ({@code kid}) parameter, 550 * {@code null} if not specified. 551 * @param customParams The custom parameters, empty map or 552 * {@code null} if none. 553 * @param parsedBase64URL The parsed Base64URL, {@code null} if the 554 * header is created from scratch. 555 */ 556 @Deprecated 557 public JWSHeader(final JWSAlgorithm alg, 558 final JOSEObjectType typ, 559 final String cty, 560 final Set<String> crit, 561 final URI jku, 562 final JWK jwk, 563 final URI x5u, 564 final Base64URL x5t, 565 final Base64URL x5t256, 566 final List<Base64> x5c, 567 final String kid, 568 final Map<String,Object> customParams, 569 final Base64URL parsedBase64URL) { 570 571 this(alg, typ, cty, crit, jku, jwk, x5u, x5t, x5t256, x5c, kid, true, customParams, parsedBase64URL); 572 } 573 574 575 /** 576 * Creates a new JSON Web Signature (JWS) header. 577 * 578 * <p>Note: Use {@link PlainHeader} to create a header with algorithm 579 * {@link Algorithm#NONE none}. 580 * 581 * @param alg The JWS algorithm ({@code alg}) parameter. 582 * Must not be "none" or {@code null}. 583 * @param typ The type ({@code typ}) parameter, 584 * {@code null} if not specified. 585 * @param cty The content type ({@code cty}) parameter, 586 * {@code null} if not specified. 587 * @param crit The names of the critical header 588 * ({@code crit}) parameters, empty set or 589 * {@code null} if none. 590 * @param jku The JSON Web Key (JWK) Set URL ({@code jku}) 591 * parameter, {@code null} if not specified. 592 * @param jwk The X.509 certificate URL ({@code jwk}) 593 * parameter, {@code null} if not specified. 594 * @param x5u The X.509 certificate URL parameter 595 * ({@code x5u}), {@code null} if not specified. 596 * @param x5t The X.509 certificate SHA-1 thumbprint 597 * ({@code x5t}) parameter, {@code null} if not 598 * specified. 599 * @param x5t256 The X.509 certificate SHA-256 thumbprint 600 * ({@code x5t#S256}) parameter, {@code null} if 601 * not specified. 602 * @param x5c The X.509 certificate chain ({@code x5c}) 603 * parameter, {@code null} if not specified. 604 * @param kid The key ID ({@code kid}) parameter, 605 * {@code null} if not specified. 606 * @param b64 {@code true} to Base64URL encode the payload 607 * for standard JWS serialisation, {@code false} 608 * for unencoded payload (RFC 7797). 609 * @param customParams The custom parameters, empty map or 610 * {@code null} if none. 611 * @param parsedBase64URL The parsed Base64URL, {@code null} if the 612 * header is created from scratch. 613 */ 614 public JWSHeader(final JWSAlgorithm alg, 615 final JOSEObjectType typ, 616 final String cty, 617 final Set<String> crit, 618 final URI jku, 619 final JWK jwk, 620 final URI x5u, 621 final Base64URL x5t, 622 final Base64URL x5t256, 623 final List<Base64> x5c, 624 final String kid, 625 final boolean b64, 626 final Map<String,Object> customParams, 627 final Base64URL parsedBase64URL) { 628 629 super(alg, typ, cty, crit, jku, jwk, x5u, x5t, x5t256, x5c, kid, customParams, parsedBase64URL); 630 631 if (alg.getName().equals(Algorithm.NONE.getName())) { 632 throw new IllegalArgumentException("The JWS algorithm \"alg\" cannot be \"none\""); 633 } 634 635 this.b64 = b64; 636 } 637 638 639 /** 640 * Deep copy constructor. 641 * 642 * @param jwsHeader The JWS header to copy. Must not be {@code null}. 643 */ 644 public JWSHeader(final JWSHeader jwsHeader) { 645 646 this( 647 jwsHeader.getAlgorithm(), 648 jwsHeader.getType(), 649 jwsHeader.getContentType(), 650 jwsHeader.getCriticalParams(), 651 jwsHeader.getJWKURL(), 652 jwsHeader.getJWK(), 653 jwsHeader.getX509CertURL(), 654 jwsHeader.getX509CertThumbprint(), 655 jwsHeader.getX509CertSHA256Thumbprint(), 656 jwsHeader.getX509CertChain(), 657 jwsHeader.getKeyID(), 658 jwsHeader.getCustomParams(), 659 jwsHeader.getParsedBase64URL() 660 ); 661 } 662 663 664 /** 665 * Gets the registered parameter names for JWS headers. 666 * 667 * @return The registered parameter names, as an unmodifiable set. 668 */ 669 public static Set<String> getRegisteredParameterNames() { 670 671 return REGISTERED_PARAMETER_NAMES; 672 } 673 674 675 /** 676 * Gets the algorithm ({@code alg}) parameter. 677 * 678 * @return The algorithm parameter. 679 */ 680 @Override 681 public JWSAlgorithm getAlgorithm() { 682 683 return (JWSAlgorithm)super.getAlgorithm(); 684 } 685 686 687 /** 688 * Returns the Base64URL-encode payload ({@code b64}) parameter. 689 * 690 * @return {@code true} to Base64URL encode the payload for standard 691 * JWS serialisation, {@code false} for unencoded payload (RFC 692 * 7797). 693 */ 694 public boolean isBase64URLEncodePayload() { 695 696 return b64; 697 } 698 699 700 @Override 701 public Set<String> getIncludedParams() { 702 Set<String> includedParams = super.getIncludedParams(); 703 if (! isBase64URLEncodePayload()) { 704 includedParams.add("b64"); 705 } 706 return includedParams; 707 } 708 709 710 @Override 711 public JSONObject toJSONObject() { 712 JSONObject o = super.toJSONObject(); 713 if (! isBase64URLEncodePayload()) { 714 o.put("b64", false); 715 } 716 return o; 717 } 718 719 720 /** 721 * Parses a JWS header from the specified JSON object. 722 * 723 * @param jsonObject The JSON object to parse. Must not be 724 * {@code null}. 725 * 726 * @return The JWS header. 727 * 728 * @throws ParseException If the specified JSON object doesn't 729 * represent a valid JWS header. 730 */ 731 public static JWSHeader parse(final JSONObject jsonObject) 732 throws ParseException { 733 734 return parse(jsonObject, null); 735 } 736 737 738 /** 739 * Parses a JWS header from the specified JSON object. 740 * 741 * @param jsonObject The JSON object to parse. Must not be 742 * {@code null}. 743 * @param parsedBase64URL The original parsed Base64URL, {@code null} 744 * if not applicable. 745 * 746 * @return The JWS header. 747 * 748 * @throws ParseException If the specified JSON object doesn't 749 * represent a valid JWS header. 750 */ 751 public static JWSHeader parse(final JSONObject jsonObject, 752 final Base64URL parsedBase64URL) 753 throws ParseException { 754 755 // Get the "alg" parameter 756 Algorithm alg = Header.parseAlgorithm(jsonObject); 757 758 if (! (alg instanceof JWSAlgorithm)) { 759 throw new ParseException("Not a JWS header", 0); 760 } 761 762 JWSHeader.Builder header = new Builder((JWSAlgorithm)alg).parsedBase64URL(parsedBase64URL); 763 764 // Parse optional + custom parameters 765 for (final String name: jsonObject.keySet()) { 766 767 if("alg".equals(name)) { 768 // skip 769 } else if("typ".equals(name)) { 770 String typValue = JSONObjectUtils.getString(jsonObject, name); 771 if (typValue != null) { 772 header = header.type(new JOSEObjectType(typValue)); 773 } 774 } else if("cty".equals(name)) { 775 header = header.contentType(JSONObjectUtils.getString(jsonObject, name)); 776 } else if("crit".equals(name)) { 777 List<String> critValues = JSONObjectUtils.getStringList(jsonObject, name); 778 if (critValues != null) { 779 header = header.criticalParams(new HashSet<>(critValues)); 780 } 781 } else if("jku".equals(name)) { 782 header = header.jwkURL(JSONObjectUtils.getURI(jsonObject, name)); 783 } else if("jwk".equals(name)) { 784 JSONObject jwkObject = JSONObjectUtils.getJSONObject(jsonObject, name); 785 if (jwkObject != null) { 786 header = header.jwk(JWK.parse(jwkObject)); 787 } 788 } else if("x5u".equals(name)) { 789 header = header.x509CertURL(JSONObjectUtils.getURI(jsonObject, name)); 790 } else if("x5t".equals(name)) { 791 header = header.x509CertThumbprint(Base64URL.from(JSONObjectUtils.getString(jsonObject, name))); 792 } else if("x5t#S256".equals(name)) { 793 header = header.x509CertSHA256Thumbprint(Base64URL.from(JSONObjectUtils.getString(jsonObject, name))); 794 } else if("x5c".equals(name)) { 795 header = header.x509CertChain(X509CertChainUtils.toBase64List(JSONObjectUtils.getJSONArray(jsonObject, name))); 796 } else if("kid".equals(name)) { 797 header = header.keyID(JSONObjectUtils.getString(jsonObject, name)); 798 } else if("b64".equals(name)) { 799 header = header.base64URLEncodePayload(JSONObjectUtils.getBoolean(jsonObject, name)); 800 } else { 801 header = header.customParam(name, jsonObject.get(name)); 802 } 803 } 804 805 return header.build(); 806 } 807 808 809 /** 810 * Parses a JWS header from the specified JSON object string. 811 * 812 * @param jsonString The JSON string to parse. Must not be 813 * {@code null}. 814 * 815 * @return The JWS header. 816 * 817 * @throws ParseException If the specified JSON object string doesn't 818 * represent a valid JWS header. 819 */ 820 public static JWSHeader parse(final String jsonString) 821 throws ParseException { 822 823 return parse(jsonString, null); 824 } 825 826 827 /** 828 * Parses a JWS header from the specified JSON object string. 829 * 830 * @param jsonString The JSON string to parse. Must not be 831 * {@code null}. 832 * @param parsedBase64URL The original parsed Base64URL, {@code null} 833 * if not applicable. 834 * 835 * @return The JWS header. 836 * 837 * @throws ParseException If the specified JSON object string doesn't 838 * represent a valid JWS header. 839 */ 840 public static JWSHeader parse(final String jsonString, 841 final Base64URL parsedBase64URL) 842 throws ParseException { 843 844 return parse(JSONObjectUtils.parse(jsonString), parsedBase64URL); 845 } 846 847 848 /** 849 * Parses a JWS header from the specified Base64URL. 850 * 851 * @param base64URL The Base64URL to parse. Must not be {@code null}. 852 * 853 * @return The JWS header. 854 * 855 * @throws ParseException If the specified Base64URL doesn't represent 856 * a valid JWS header. 857 */ 858 public static JWSHeader parse(final Base64URL base64URL) 859 throws ParseException { 860 861 return parse(base64URL.decodeToString(), base64URL); 862 } 863}