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;
026
027import com.nimbusds.jose.jwk.JWK;
028import com.nimbusds.jose.util.Base64;
029import com.nimbusds.jose.util.Base64URL;
030import com.nimbusds.jose.util.JSONObjectUtils;
031import com.nimbusds.jose.util.X509CertChainUtils;
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 2021-06-05
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.isBase64URLEncodePayload(),
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 Map<String, Object> toJSONObject() {
713                Map<String, Object> 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 Map<String, Object> 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 Map<String, Object> 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                                Map<String, Object> 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}