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}