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}