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}