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