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