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 == null) {
636                        throw new IllegalArgumentException("The algorithm \"alg\" header parameter must not be null");
637                }
638
639                if (alg.getName().equals(Algorithm.NONE.getName())) {
640                        throw new IllegalArgumentException("The JWS algorithm \"alg\" cannot be \"none\"");
641                }
642                
643                this.b64 = b64;
644        }
645
646
647        /**
648         * Deep copy constructor.
649         *
650         * @param jwsHeader The JWS header to copy. Must not be {@code null}.
651         */
652        public JWSHeader(final JWSHeader jwsHeader) {
653
654                this(
655                        jwsHeader.getAlgorithm(),
656                        jwsHeader.getType(),
657                        jwsHeader.getContentType(),
658                        jwsHeader.getCriticalParams(),
659                        jwsHeader.getJWKURL(),
660                        jwsHeader.getJWK(),
661                        jwsHeader.getX509CertURL(),
662                        jwsHeader.getX509CertThumbprint(),
663                        jwsHeader.getX509CertSHA256Thumbprint(),
664                        jwsHeader.getX509CertChain(),
665                        jwsHeader.getKeyID(),
666                        jwsHeader.isBase64URLEncodePayload(),
667                        jwsHeader.getCustomParams(),
668                        jwsHeader.getParsedBase64URL()
669                );
670        }
671
672
673        /**
674         * Gets the registered parameter names for JWS headers.
675         *
676         * @return The registered parameter names, as an unmodifiable set.
677         */
678        public static Set<String> getRegisteredParameterNames() {
679
680                return REGISTERED_PARAMETER_NAMES;
681        }
682
683
684        /**
685         * Gets the algorithm ({@code alg}) parameter.
686         *
687         * @return The algorithm parameter.
688         */
689        @Override
690        public JWSAlgorithm getAlgorithm() {
691
692                return (JWSAlgorithm)super.getAlgorithm();
693        }
694        
695        
696        /**
697         * Returns the Base64URL-encode payload ({@code b64}) parameter.
698         *
699         * @return {@code true} to Base64URL encode the payload for standard
700         *         JWS serialisation, {@code false} for unencoded payload (RFC
701         *         7797).
702         */
703        public boolean isBase64URLEncodePayload() {
704                
705                return b64;
706        }
707        
708        
709        @Override
710        public Set<String> getIncludedParams() {
711                Set<String> includedParams = super.getIncludedParams();
712                if (! isBase64URLEncodePayload()) {
713                        includedParams.add(HeaderParameterNames.BASE64_URL_ENCODE_PAYLOAD);
714                }
715                return includedParams;
716        }
717        
718        
719        @Override
720        public Map<String, Object> toJSONObject() {
721                Map<String, Object> o = super.toJSONObject();
722                if (! isBase64URLEncodePayload()) {
723                        o.put(HeaderParameterNames.BASE64_URL_ENCODE_PAYLOAD, false);
724                }
725                return o;
726        }
727        
728        
729        /**
730         * Parses a JWS header from the specified JSON object.
731         *
732         * @param jsonObject The JSON object to parse. Must not be
733         *                   {@code null}.
734         *
735         * @return The JWS header.
736         *
737         * @throws ParseException If the specified JSON object doesn't
738         *                        represent a valid JWS header.
739         */
740        public static JWSHeader parse(final Map<String, Object> jsonObject)
741                throws ParseException {
742
743                return parse(jsonObject, null);
744        }
745
746
747        /**
748         * Parses a JWS header from the specified JSON object.
749         *
750         * @param jsonObject      The JSON object to parse. Must not be
751         *                        {@code null}.
752         * @param parsedBase64URL The original parsed Base64URL, {@code null}
753         *                        if not applicable.
754         *
755         * @return The JWS header.
756         *
757         * @throws ParseException If the specified JSON object doesn't 
758         *                        represent a valid JWS header.
759         */
760        public static JWSHeader parse(final Map<String, Object> jsonObject,
761                                      final Base64URL parsedBase64URL)
762                throws ParseException {
763
764                // Get the "alg" parameter
765                Algorithm alg = Header.parseAlgorithm(jsonObject);
766
767                if (! (alg instanceof JWSAlgorithm)) {
768                        throw new ParseException("Not a JWS header", 0);
769                }
770
771                JWSHeader.Builder header = new Builder((JWSAlgorithm)alg).parsedBase64URL(parsedBase64URL);
772
773                // Parse optional + custom parameters
774                for (final String name: jsonObject.keySet()) {
775                        
776                        if(HeaderParameterNames.ALGORITHM.equals(name)) {
777                                // skip
778                        } else if(HeaderParameterNames.TYPE.equals(name)) {
779                                String typValue = JSONObjectUtils.getString(jsonObject, name);
780                                if (typValue != null) {
781                                        header = header.type(new JOSEObjectType(typValue));
782                                }
783                        } else if(HeaderParameterNames.CONTENT_TYPE.equals(name)) {
784                                header = header.contentType(JSONObjectUtils.getString(jsonObject, name));
785                        } else if(HeaderParameterNames.CRITICAL.equals(name)) {
786                                List<String> critValues = JSONObjectUtils.getStringList(jsonObject, name);
787                                if (critValues != null) {
788                                        header = header.criticalParams(new HashSet<>(critValues));
789                                }
790                        } else if(HeaderParameterNames.JWK_SET_URL.equals(name)) {
791                                header = header.jwkURL(JSONObjectUtils.getURI(jsonObject, name));
792                        } else if(HeaderParameterNames.JWK.equals(name)) {
793                                header = header.jwk(CommonSEHeader.parsePublicJWK(JSONObjectUtils.getJSONObject(jsonObject, name)));
794                        } else if(HeaderParameterNames.X_509_CERT_URL.equals(name)) {
795                                header = header.x509CertURL(JSONObjectUtils.getURI(jsonObject, name));
796                        } else if(HeaderParameterNames.X_509_CERT_SHA_1_THUMBPRINT.equals(name)) {
797                                header = header.x509CertThumbprint(Base64URL.from(JSONObjectUtils.getString(jsonObject, name)));
798                        } else if(HeaderParameterNames.X_509_CERT_SHA_256_THUMBPRINT.equals(name)) {
799                                header = header.x509CertSHA256Thumbprint(Base64URL.from(JSONObjectUtils.getString(jsonObject, name)));
800                        } else if(HeaderParameterNames.X_509_CERT_CHAIN.equals(name)) {
801                                header = header.x509CertChain(X509CertChainUtils.toBase64List(JSONObjectUtils.getJSONArray(jsonObject, name)));
802                        } else if(HeaderParameterNames.KEY_ID.equals(name)) {
803                                header = header.keyID(JSONObjectUtils.getString(jsonObject, name));
804                        } else if(HeaderParameterNames.BASE64_URL_ENCODE_PAYLOAD.equals(name)) {
805                                header = header.base64URLEncodePayload(JSONObjectUtils.getBoolean(jsonObject, name));
806                        } else {
807                                header = header.customParam(name, jsonObject.get(name));
808                        }
809                }
810
811                return header.build();
812        }
813
814
815        /**
816         * Parses a JWS header from the specified JSON object string.
817         *
818         * @param jsonString The JSON string to parse. Must not be
819         *                   {@code null}.
820         *
821         * @return The JWS header.
822         *
823         * @throws ParseException If the specified JSON object string doesn't
824         *                        represent a valid JWS header.
825         */
826        public static JWSHeader parse(final String jsonString)
827                throws ParseException {
828
829                return parse(jsonString, null);
830        }
831
832
833        /**
834         * Parses a JWS header from the specified JSON object string.
835         *
836         * @param jsonString      The JSON string to parse. Must not be
837         *                        {@code null}.
838         * @param parsedBase64URL The original parsed Base64URL, {@code null}
839         *                        if not applicable.
840         *
841         * @return The JWS header.
842         *
843         * @throws ParseException If the specified JSON object string doesn't 
844         *                        represent a valid JWS header.
845         */
846        public static JWSHeader parse(final String jsonString,
847                                      final Base64URL parsedBase64URL)
848                throws ParseException {
849
850                return parse(JSONObjectUtils.parse(jsonString, MAX_HEADER_STRING_LENGTH), parsedBase64URL);
851        }
852
853
854        /**
855         * Parses a JWS header from the specified Base64URL.
856         *
857         * @param base64URL The Base64URL to parse. Must not be {@code null}.
858         *
859         * @return The JWS header.
860         *
861         * @throws ParseException If the specified Base64URL doesn't represent
862         *                        a valid JWS header.
863         */
864        public static JWSHeader parse(final Base64URL base64URL)
865                throws ParseException {
866
867                return parse(base64URL.decodeToString(), base64URL);
868        }
869}