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