001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2013-2015 ForgeRock AS.
015 */
016
017package org.forgerock.json.jose.jws;
018
019import static org.forgerock.json.jose.jws.JwsHeaderKey.CRIT;
020import static org.forgerock.json.jose.jws.JwsHeaderKey.CTY;
021import static org.forgerock.json.jose.jws.JwsHeaderKey.JKU;
022import static org.forgerock.json.jose.jws.JwsHeaderKey.JWK;
023import static org.forgerock.json.jose.jws.JwsHeaderKey.KID;
024import static org.forgerock.json.jose.jws.JwsHeaderKey.X5C;
025import static org.forgerock.json.jose.jws.JwsHeaderKey.X5T;
026import static org.forgerock.json.jose.jws.JwsHeaderKey.X5U;
027import static org.forgerock.json.jose.jws.JwsHeaderKey.getHeaderKey;
028
029import java.net.MalformedURLException;
030import java.net.URL;
031import java.util.ArrayList;
032import java.util.List;
033import java.util.Map;
034
035import org.forgerock.json.jose.exceptions.JwtRuntimeException;
036import org.forgerock.json.jose.jwk.JWK;
037import org.forgerock.json.jose.jwt.JwtHeader;
038import org.forgerock.json.jose.utils.Utils;
039import org.forgerock.util.encode.Base64;
040
041/**
042 * A base implementation for the common security header parameters shared by the JWS and JWE headers.
043 *
044 * @since 2.0.0
045 */
046public abstract class JwtSecureHeader extends JwtHeader {
047
048    /**
049     * Constructs a new, empty JwtSecureHeader.
050     */
051    public JwtSecureHeader() {
052    }
053
054    /**
055     * Constructs a new JwtSecureHeader, with its parameters set to the contents of the given Map.
056     *
057     * @param headers A Map containing the parameters to be set in the header.
058     */
059    public JwtSecureHeader(Map<String, Object> headers) {
060        setParameters(headers);
061    }
062
063    /**
064     * Sets the JWK Set URL header parameter for this JWS.
065     * <p>
066     * A URI that refers to a resource for a set of JSON-encoded public keys, one of which corresponds to the key used
067     * to digitally sign the JWS.
068     * <p>
069     * The keys MUST be encoded as a JSON Web Key Set (JWK Set).
070     * <p>
071     * The protocol used to acquire the resource MUST provide integrity protection and the identity of the server MUST
072     * be validated.
073     *
074     * @param jwkSetUrl The JWK Set URL.
075     */
076    public void setJwkSetUrl(URL jwkSetUrl) {
077        put(JKU.value(), new String(jwkSetUrl.toString()));
078    }
079
080    /**
081     * Gets the JWK Set URL header parameter for this JWS.
082     *
083     * @return The JWK Set URL.
084     */
085    public URL getJwkSetUrl() {
086        try {
087            return get(JKU.value()).asURI().toURL();
088        } catch (MalformedURLException e) {
089            throw new JwtRuntimeException(e);
090        }
091    }
092
093    /**
094     * Sets the JSON Web Key header parameter for this JWS.
095     * <p>
096     * The public key that corresponds to the key used to digitally sign the JWS. This key is represented as a JSON Web
097     * Key (JWK).
098     *
099     * @param jsonWebKey The JSON Web Key.
100     */
101    public void setJsonWebKey(JWK jsonWebKey) {
102        put(JWK.value(), jsonWebKey);
103    }
104
105    /**
106     * Gets the JSON Web Key header parameter for this JWS.
107     *
108     * @return The JSON Web Key.
109     */
110    public JWK getJsonWebKey() {
111        return (JWK) get(JWK.value()).getObject();
112    }
113
114    /**
115     * Sets the X.509 URL header parameter for this JWS.
116     * <p>
117     * A URI that refers to a resource for the X.509 public key certificate or certificate chain corresponding to the
118     * key used to digitally sign the JWS.
119     * <p>
120     * The certificate containing the public key corresponding to the key used to digitally sign the JWS MUST be the
121     * first certificate. This MAY be followed by additional certificates, with each subsequent certificate being the
122     * one used to certify the previous one.
123     * <p>
124     * The protocol used to acquire the resource MUST provide integrity protection and the identity of the server MUST
125     * be validated.
126     *
127     * @param x509Url The X.509 URL.
128     */
129    public void setX509Url(URL x509Url) {
130        put(X5U.value(), new String(x509Url.toString()));
131    }
132
133    /**
134     * Gets the X.509 URL header parameter for this JWS.
135     *
136     * @return The X.509 URL.
137     */
138    public URL getX509Url() {
139        try {
140            return get(X5U.value()).asURI().toURL();
141        } catch (MalformedURLException e) {
142            throw new JwtRuntimeException(e);
143        }
144    }
145
146    /**
147     * Sets the X.509 Certificate Thumbprint header parameter for this JWS.
148     * <p>
149     * A base64url encoded SHA-1 thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate corresponding
150     * to the key used to digitally sign the JWS.
151     * <p>
152     * This method will perform the base64url encoding so the x509CertificateThumbprint must be the SHA-1 digest.
153     *
154     * @param x509CertificateThumbprint The X.509 Certificate Thumbprint.
155     */
156    public void setX509CertificateThumbprint(String x509CertificateThumbprint) {
157        put(X5T.value(), Utils.base64urlEncode(x509CertificateThumbprint));
158    }
159
160    /**
161     * Gets the X.509 Certificate Thumbprint header parameter for this JWS.
162     *
163     * @return The X.509 Certificate Thumbprint.
164     */
165    public String getX509CertificateThumbprint() {
166        return get(X5T.value()).asString();
167    }
168
169    /**
170     * Sets the X.509 Certificate Chain header parameter for this JWS.
171     * <p>
172     * Contains the list of X.509 public key certificate or certificate chain corresponding to the key used to
173     * digitally sign the JWS.
174     * Each entry in the list is a base64 encoded DER PKIX certificate value.
175     * This method will perform the base64 encoding of each entry so the entries in the list must be the DER PKIX
176     * certificate values.
177     * <p>
178     * The certificate containing the public key corresponding to the key used to digitally sign the JWS MUST be the
179     * first certificate. This MAY be followed by additional certificates, with each subsequent certificate being the
180     * one used to certify the previous one.
181     * <p>
182     *
183     * @param x509CertificateChain The X.509 Certificate Chain.
184     */
185    public void setX509CertificateChain(List<String> x509CertificateChain) {
186        List<String> encodedCertChain = new ArrayList<>();
187        for (String x509Cert : x509CertificateChain) {
188            encodedCertChain.add(Base64.encode(x509Cert.getBytes(Utils.CHARSET)));
189        }
190        put(X5C.value(), encodedCertChain);
191    }
192
193    /**
194     * Gets the X.509 Certificate Chain header parameter for this JWS.
195     *
196     * @return The X.509 Certificate Chain.
197     */
198    public List<String> getX509CertificateChain() {
199        return get(X5C.value()).asList(String.class);
200    }
201
202    /**
203     * Sets the Key ID header parameter for this JWS.
204     * <p>
205     * Indicates which key was used to secure the JWS, allowing originators to explicitly signal a change of key to
206     * recipients.
207     *
208     * @param keyId The Key ID.
209     */
210    public void setKeyId(String keyId) {
211        put(KID.value(), keyId);
212    }
213
214    /**
215     * Gets the Key ID header parameter for this JWS.
216     *
217     * @return The Key ID.
218     */
219    public String getKeyId() {
220        return get(KID.value()).asString();
221    }
222
223    /**
224     * Sets the content type header parameter for this JWS.
225     * <p>
226     * Declares the type of the secured content (the Payload).
227     *
228     * @param contentType The content type of this JWS' payload.
229     */
230    public void setContentType(String contentType) {
231        put(CTY.value(), contentType);
232    }
233
234    /**
235     * Gets the content type header parameter for this JWS.
236     *
237     * @return The content type of this JWS' payload.
238     */
239    public String getContentType() {
240        return get(CTY.value()).asString();
241    }
242
243    /**
244     * Sets the critical header parameters for this JWS.
245     * <p>
246     * This header parameter indicates that extensions to the JWS specification are being used that MUST be understood
247     * and processed.
248     * <p>
249     * The criticalHeaders parameter cannot be an empty list.
250     *
251     * @param criticalHeaders A List of the critical parameters.
252     */
253    public void setCriticalHeaders(List<String> criticalHeaders) {
254        if (criticalHeaders != null && criticalHeaders.isEmpty()) {
255            throw new JwtRuntimeException("Critical Headers parameter cannot be an empty list");
256        }
257        put(CRIT.value(), criticalHeaders);
258    }
259
260    /**
261     * Gets the critical header parameters for this JWS.
262     *
263     * @return A List of the critical parameters.
264     */
265    public List<String> getCriticalHeaders() {
266        return get(CRIT.value()).asList(String.class);
267    }
268
269    /**
270     * {@inheritDoc}
271     */
272    @SuppressWarnings("unchecked")
273    @Override
274    public void setParameter(String key, Object value) {
275        JwsHeaderKey headerKey = getHeaderKey(key.toUpperCase());
276
277        switch (headerKey) {
278        case JKU: {
279            checkValueIsOfType(value, URL.class);
280            setJwkSetUrl((URL) value);
281            break;
282        }
283        case JWK: {
284            checkValueIsOfType(value, JWK.class);
285            setJsonWebKey((JWK) value);
286            break;
287        }
288        case X5U: {
289            checkValueIsOfType(value, URL.class);
290            setX509Url((URL) value);
291            break;
292        }
293        case X5T: {
294            checkValueIsOfType(value, String.class);
295            setX509CertificateThumbprint((String) value);
296            break;
297        }
298        case X5C: {
299            checkValueIsOfType(value, List.class);
300            checkListValuesAreOfType((List<?>) value, String.class);
301            setX509CertificateChain((List<String>) value);
302            break;
303        }
304        case KID: {
305            checkValueIsOfType(value, String.class);
306            setKeyId((String) value);
307            break;
308        }
309        case CTY: {
310            checkValueIsOfType(value, String.class);
311            setContentType((String) value);
312            break;
313        }
314        case CRIT: {
315            checkValueIsOfType(value, List.class);
316            checkListValuesAreOfType((List<?>) value, String.class);
317            setCriticalHeaders((List<String>) value);
318            break;
319        }
320        default: {
321            super.setParameter(key, value);
322        }
323        }
324    }
325
326    /**
327     * {@inheritDoc}
328     */
329    @Override
330    public Object getParameter(String key) {
331        JwsHeaderKey headerKey = getHeaderKey(key.toUpperCase());
332
333        Object value;
334
335        switch (headerKey) {
336        case JKU: {
337            value = getJwkSetUrl();
338            break;
339        }
340        case JWK: {
341            value = getJsonWebKey();
342            break;
343        }
344        case X5U: {
345            value = getX509Url();
346            break;
347        }
348        case X5T: {
349            value = getX509CertificateThumbprint();
350            break;
351        }
352        case X5C: {
353            value = getX509CertificateChain();
354            break;
355        }
356        case KID: {
357            value = getKeyId();
358            break;
359        }
360        case CTY: {
361            value = getContentType();
362            break;
363        }
364        case CRIT: {
365            value = getCriticalHeaders();
366            break;
367        }
368        default: {
369            value = super.getParameter(key);
370        }
371        }
372
373        return value;
374    }
375}