View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2013-2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.json.jose.jws;
18  
19  import static org.forgerock.json.jose.jws.JwsHeaderKey.CRIT;
20  import static org.forgerock.json.jose.jws.JwsHeaderKey.CTY;
21  import static org.forgerock.json.jose.jws.JwsHeaderKey.JKU;
22  import static org.forgerock.json.jose.jws.JwsHeaderKey.JWK;
23  import static org.forgerock.json.jose.jws.JwsHeaderKey.KID;
24  import static org.forgerock.json.jose.jws.JwsHeaderKey.X5C;
25  import static org.forgerock.json.jose.jws.JwsHeaderKey.X5T;
26  import static org.forgerock.json.jose.jws.JwsHeaderKey.X5U;
27  import static org.forgerock.json.jose.jws.JwsHeaderKey.getHeaderKey;
28  
29  import java.net.MalformedURLException;
30  import java.net.URL;
31  import java.util.ArrayList;
32  import java.util.List;
33  import java.util.Map;
34  
35  import org.forgerock.json.jose.exceptions.JwtRuntimeException;
36  import org.forgerock.json.jose.jwe.CompressionAlgorithm;
37  import org.forgerock.json.jose.jwk.JWK;
38  import org.forgerock.json.jose.jwt.JwtHeader;
39  import org.forgerock.json.jose.utils.Utils;
40  import org.forgerock.util.encode.Base64;
41  
42  /**
43   * A base implementation for the common security header parameters shared by the JWS and JWE headers.
44   *
45   * @since 2.0.0
46   */
47  public abstract class JwtSecureHeader extends JwtHeader {
48      private static final String COMPRESSION_ALGORITHM_HEADER_KEY = "zip";
49  
50      /**
51       * Constructs a new, empty JwtSecureHeader.
52       */
53      public JwtSecureHeader() {
54      }
55  
56      /**
57       * Constructs a new JwtSecureHeader, with its parameters set to the contents of the given Map.
58       *
59       * @param headers A Map containing the parameters to be set in the header.
60       */
61      public JwtSecureHeader(Map<String, Object> headers) {
62          setParameters(headers);
63      }
64  
65      /**
66       * Sets the JWK Set URL header parameter for this JWS.
67       * <p>
68       * A URI that refers to a resource for a set of JSON-encoded public keys, one of which corresponds to the key used
69       * to digitally sign the JWS.
70       * <p>
71       * The keys MUST be encoded as a JSON Web Key Set (JWK Set).
72       * <p>
73       * The protocol used to acquire the resource MUST provide integrity protection and the identity of the server MUST
74       * be validated.
75       *
76       * @param jwkSetUrl The JWK Set URL.
77       */
78      public void setJwkSetUrl(URL jwkSetUrl) {
79          put(JKU.value(), new String(jwkSetUrl.toString()));
80      }
81  
82      /**
83       * Gets the JWK Set URL header parameter for this JWS.
84       *
85       * @return The JWK Set URL.
86       */
87      public URL getJwkSetUrl() {
88          try {
89              String url = get(JKU.value()).asString();
90              return url != null
91                      ? new URL(url)
92                      : null;
93          } catch (MalformedURLException e) {
94              throw new JwtRuntimeException(e);
95          }
96      }
97  
98      /**
99       * Sets the JSON Web Key header parameter for this JWS.
100      * <p>
101      * The public key that corresponds to the key used to digitally sign the JWS. This key is represented as a JSON Web
102      * Key (JWK).
103      *
104      * @param jsonWebKey The JSON Web Key.
105      */
106     public void setJsonWebKey(JWK jsonWebKey) {
107         put(JWK.value(), jsonWebKey);
108     }
109 
110     /**
111      * Gets the JSON Web Key header parameter for this JWS.
112      *
113      * @return The JSON Web Key.
114      */
115     public JWK getJsonWebKey() {
116         return (JWK) get(JWK.value()).getObject();
117     }
118 
119     /**
120      * Sets the X.509 URL header parameter for this JWS.
121      * <p>
122      * A URI that refers to a resource for the X.509 public key certificate or certificate chain corresponding to the
123      * key used to digitally sign the JWS.
124      * <p>
125      * The certificate containing the public key corresponding to the key used to digitally sign the JWS MUST be the
126      * first certificate. This MAY be followed by additional certificates, with each subsequent certificate being the
127      * one used to certify the previous one.
128      * <p>
129      * The protocol used to acquire the resource MUST provide integrity protection and the identity of the server MUST
130      * be validated.
131      *
132      * @param x509Url The X.509 URL.
133      */
134     public void setX509Url(URL x509Url) {
135         put(X5U.value(), new String(x509Url.toString()));
136     }
137 
138     /**
139      * Gets the X.509 URL header parameter for this JWS.
140      *
141      * @return The X.509 URL.
142      */
143     public URL getX509Url() {
144         try {
145             String url = get(X5U.value()).asString();
146             return url != null
147                     ? new URL(url)
148                     : null;
149         } catch (MalformedURLException e) {
150             throw new JwtRuntimeException(e);
151         }
152     }
153 
154     /**
155      * Sets the X.509 Certificate Thumbprint header parameter for this JWS.
156      * <p>
157      * A base64url encoded SHA-1 thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate corresponding
158      * to the key used to digitally sign the JWS.
159      * <p>
160      * This method will perform the base64url encoding so the x509CertificateThumbprint must be the SHA-1 digest.
161      *
162      * @param x509CertificateThumbprint The X.509 Certificate Thumbprint.
163      */
164     public void setX509CertificateThumbprint(String x509CertificateThumbprint) {
165         put(X5T.value(), Utils.base64urlEncode(x509CertificateThumbprint));
166     }
167 
168     /**
169      * Gets the X.509 Certificate Thumbprint header parameter for this JWS.
170      *
171      * @return The X.509 Certificate Thumbprint.
172      */
173     public String getX509CertificateThumbprint() {
174         return get(X5T.value()).asString();
175     }
176 
177     /**
178      * Sets the X.509 Certificate Chain header parameter for this JWS.
179      * <p>
180      * Contains the list of X.509 public key certificate or certificate chain corresponding to the key used to
181      * digitally sign the JWS.
182      * Each entry in the list is a base64 encoded DER PKIX certificate value.
183      * This method will perform the base64 encoding of each entry so the entries in the list must be the DER PKIX
184      * certificate values.
185      * <p>
186      * The certificate containing the public key corresponding to the key used to digitally sign the JWS MUST be the
187      * first certificate. This MAY be followed by additional certificates, with each subsequent certificate being the
188      * one used to certify the previous one.
189      * <p>
190      *
191      * @param x509CertificateChain The X.509 Certificate Chain.
192      */
193     public void setX509CertificateChain(List<String> x509CertificateChain) {
194         List<String> encodedCertChain = new ArrayList<>();
195         for (String x509Cert : x509CertificateChain) {
196             encodedCertChain.add(Base64.encode(x509Cert.getBytes(Utils.CHARSET)));
197         }
198         put(X5C.value(), encodedCertChain);
199     }
200 
201     /**
202      * Gets the X.509 Certificate Chain header parameter for this JWS.
203      *
204      * @return The X.509 Certificate Chain.
205      */
206     public List<String> getX509CertificateChain() {
207         return get(X5C.value()).asList(String.class);
208     }
209 
210     /**
211      * Sets the Key ID header parameter for this JWS.
212      * <p>
213      * Indicates which key was used to secure the JWS, allowing originators to explicitly signal a change of key to
214      * recipients.
215      *
216      * @param keyId The Key ID.
217      */
218     public void setKeyId(String keyId) {
219         put(KID.value(), keyId);
220     }
221 
222     /**
223      * Gets the Key ID header parameter for this JWS.
224      *
225      * @return The Key ID.
226      */
227     public String getKeyId() {
228         return get(KID.value()).asString();
229     }
230 
231     /**
232      * Sets the content type header parameter for this JWS.
233      * <p>
234      * Declares the type of the secured content (the Payload).
235      *
236      * @param contentType The content type of this JWS' payload.
237      */
238     public void setContentType(String contentType) {
239         put(CTY.value(), contentType);
240     }
241 
242     /**
243      * Gets the content type header parameter for this JWS.
244      *
245      * @return The content type of this JWS' payload.
246      */
247     public String getContentType() {
248         return get(CTY.value()).asString();
249     }
250 
251     /**
252      * Sets the critical header parameters for this JWS.
253      * <p>
254      * This header parameter indicates that extensions to the JWS specification are being used that MUST be understood
255      * and processed.
256      * <p>
257      * The criticalHeaders parameter cannot be an empty list.
258      *
259      * @param criticalHeaders A List of the critical parameters.
260      */
261     public void setCriticalHeaders(List<String> criticalHeaders) {
262         if (criticalHeaders != null && criticalHeaders.isEmpty()) {
263             throw new JwtRuntimeException("Critical Headers parameter cannot be an empty list");
264         }
265         put(CRIT.value(), criticalHeaders);
266     }
267 
268     /**
269      * Gets the critical header parameters for this JWS.
270      *
271      * @return A List of the critical parameters.
272      */
273     public List<String> getCriticalHeaders() {
274         return get(CRIT.value()).asList(String.class);
275     }
276 
277     /**
278      * {@inheritDoc}
279      */
280     @SuppressWarnings("unchecked")
281     @Override
282     public void setParameter(String key, Object value) {
283         JwsHeaderKey headerKey = getHeaderKey(key.toUpperCase());
284 
285         switch (headerKey) {
286         case JKU: {
287             checkValueIsOfType(value, URL.class);
288             setJwkSetUrl((URL) value);
289             break;
290         }
291         case JWK: {
292             checkValueIsOfType(value, JWK.class);
293             setJsonWebKey((JWK) value);
294             break;
295         }
296         case X5U: {
297             checkValueIsOfType(value, URL.class);
298             setX509Url((URL) value);
299             break;
300         }
301         case X5T: {
302             checkValueIsOfType(value, String.class);
303             setX509CertificateThumbprint((String) value);
304             break;
305         }
306         case X5C: {
307             checkValueIsOfType(value, List.class);
308             checkListValuesAreOfType((List<?>) value, String.class);
309             setX509CertificateChain((List<String>) value);
310             break;
311         }
312         case KID: {
313             checkValueIsOfType(value, String.class);
314             setKeyId((String) value);
315             break;
316         }
317         case CTY: {
318             checkValueIsOfType(value, String.class);
319             setContentType((String) value);
320             break;
321         }
322         case CRIT: {
323             checkValueIsOfType(value, List.class);
324             checkListValuesAreOfType((List<?>) value, String.class);
325             setCriticalHeaders((List<String>) value);
326             break;
327         }
328         default: {
329             super.setParameter(key, value);
330         }
331         }
332     }
333 
334     /**
335      * {@inheritDoc}
336      */
337     @Override
338     public Object getParameter(String key) {
339         JwsHeaderKey headerKey = getHeaderKey(key.toUpperCase());
340 
341         Object value;
342 
343         switch (headerKey) {
344         case JKU: {
345             value = getJwkSetUrl();
346             break;
347         }
348         case JWK: {
349             value = getJsonWebKey();
350             break;
351         }
352         case X5U: {
353             value = getX509Url();
354             break;
355         }
356         case X5T: {
357             value = getX509CertificateThumbprint();
358             break;
359         }
360         case X5C: {
361             value = getX509CertificateChain();
362             break;
363         }
364         case KID: {
365             value = getKeyId();
366             break;
367         }
368         case CTY: {
369             value = getContentType();
370             break;
371         }
372         case CRIT: {
373             value = getCriticalHeaders();
374             break;
375         }
376         default: {
377             value = super.getParameter(key);
378         }
379         }
380 
381         return value;
382     }
383 
384     /**
385      * Sets the Compression Algorithm header parameter for this JWE.
386      * <p>
387      * If present, the value of the Compression Algorithm header parameter MUST be CompressionAlgorithm constant DEF.
388      *
389      * @param compressionAlgorithm The Compression Algorithm.
390      */
391     public void setCompressionAlgorithm(CompressionAlgorithm compressionAlgorithm) {
392         put(COMPRESSION_ALGORITHM_HEADER_KEY, compressionAlgorithm.toString());
393     }
394 
395     /**
396      * Gets the Compression Algorithm header parameter for this JWE.
397      *
398      * @return The Compression Algorithm.
399      */
400     public CompressionAlgorithm getCompressionAlgorithm() {
401         String compressionAlgorithm = get(COMPRESSION_ALGORITHM_HEADER_KEY).asString();
402         if (compressionAlgorithm == null) {
403             return CompressionAlgorithm.NONE;
404         } else {
405             return CompressionAlgorithm.valueOf(compressionAlgorithm);
406         }
407     }
408 
409 }