001 package com.nimbusds.jose; 002 003 004 import java.text.ParseException; 005 import java.util.Collections; 006 import java.util.HashSet; 007 import java.util.Set; 008 009 import net.minidev.json.JSONObject; 010 011 import com.nimbusds.jose.jwk.JWK; 012 import com.nimbusds.jose.util.Base64URL; 013 import com.nimbusds.jose.util.JSONObjectUtils; 014 015 016 /** 017 * JSON Web Signature (JWS) header. 018 * 019 * <p>Supports all {@link #getReservedParameterNames reserved header parameters} 020 * of the JWS specification: 021 * 022 * <ul> 023 * <li>alg 024 * <li>jku 025 * <li>jwk 026 * <li>x5u 027 * <li>x5t 028 * <li>x5c 029 * <li>kid 030 * <li>typ 031 * <li>cty 032 * <li>crit 033 * </ul> 034 * 035 * <p>The header may also carry {@link #setCustomParameters custom parameters}; 036 * these will be serialised and parsed along the reserved ones. 037 * 038 * <p>Example header of a JSON Web Signature (JWS) object using the 039 * {@link JWSAlgorithm#HS256 HMAC SHA-256 algorithm}: 040 * 041 * <pre> 042 * { 043 * "alg" : "HS256" 044 * } 045 * </pre> 046 * 047 * @author Vladimir Dzhuvinov 048 * @version $version$ (2013-05-07) 049 */ 050 public class JWSHeader extends CommonSEHeader implements ReadOnlyJWSHeader { 051 052 053 /** 054 * The reserved parameter names. 055 */ 056 private static final Set<String> RESERVED_PARAMETER_NAMES; 057 058 059 /** 060 * Initialises the reserved parameter name set. 061 */ 062 static { 063 Set<String> p = new HashSet<String>(); 064 065 p.add("alg"); 066 p.add("jku"); 067 p.add("jwk"); 068 p.add("x5u"); 069 p.add("x5t"); 070 p.add("x5c"); 071 p.add("kid"); 072 p.add("typ"); 073 p.add("cty"); 074 p.add("crit"); 075 076 RESERVED_PARAMETER_NAMES = Collections.unmodifiableSet(p); 077 } 078 079 080 /** 081 * Creates a new JSON Web Signature (JWS) header. 082 * 083 * @param alg The JWS algorithm. Must not be {@code null}. 084 */ 085 public JWSHeader(final JWSAlgorithm alg) { 086 087 super(alg); 088 } 089 090 091 /** 092 * Gets the reserved parameter names for JWS headers. 093 * 094 * @return The reserved parameter names, as an unmodifiable set. 095 */ 096 public static Set<String> getReservedParameterNames() { 097 098 return RESERVED_PARAMETER_NAMES; 099 } 100 101 102 @Override 103 public JWSAlgorithm getAlgorithm() { 104 105 return (JWSAlgorithm)alg; 106 } 107 108 109 /** 110 * @throws IllegalArgumentException If the specified parameter name 111 * matches a reserved parameter name. 112 */ 113 @Override 114 public void setCustomParameter(final String name, final Object value) { 115 116 if (getReservedParameterNames().contains(name)) { 117 throw new IllegalArgumentException("The parameter name \"" + name + "\" matches a reserved name"); 118 } 119 120 super.setCustomParameter(name, value); 121 } 122 123 124 @Override 125 public Set<String> getIncludedParameters() { 126 127 Set<String> includedParameters = 128 new HashSet<String>(getCustomParameters().keySet()); 129 130 includedParameters.add("alg"); 131 132 if (getType() != null) { 133 includedParameters.add("typ"); 134 } 135 136 if (getContentType() != null) { 137 includedParameters.add("cty"); 138 } 139 140 if (getCriticalHeaders() != null && ! getCriticalHeaders().isEmpty()) { 141 includedParameters.add("crit"); 142 } 143 144 if (getJWKURL() != null) { 145 includedParameters.add("jku"); 146 } 147 148 if (getJWK() != null) { 149 includedParameters.add("jwk"); 150 } 151 152 if (getX509CertURL() != null) { 153 includedParameters.add("x5u"); 154 } 155 156 if (getX509CertThumbprint() != null) { 157 includedParameters.add("x5t"); 158 } 159 160 if (getX509CertChain() != null) { 161 includedParameters.add("x5c"); 162 } 163 164 if (getKeyID() != null) { 165 includedParameters.add("kid"); 166 } 167 168 return includedParameters; 169 } 170 171 172 /** 173 * Parses a JWS header from the specified JSON object. 174 * 175 * @param json The JSON object to parse. Must not be {@code null}. 176 * 177 * @return The JWS header. 178 * 179 * @throws ParseException If the specified JSON object doesn't 180 * represent a valid JWS header. 181 */ 182 public static JWSHeader parse(final JSONObject json) 183 throws ParseException { 184 185 // Get the "alg" parameter 186 Algorithm alg = Header.parseAlgorithm(json); 187 188 if (! (alg instanceof JWSAlgorithm)) { 189 throw new ParseException("The algorithm \"alg\" header parameter must be for signatures", 0); 190 } 191 192 // Create a minimal header 193 JWSHeader h = new JWSHeader((JWSAlgorithm)alg); 194 195 // Parse optional + custom parameters 196 for (final String name: json.keySet()) { 197 198 if (name.equals("alg")) { 199 continue; // Skip 200 } else if (name.equals("typ")) { 201 h.setType(new JOSEObjectType(JSONObjectUtils.getString(json, name))); 202 } else if (name.equals("cty")) { 203 h.setContentType(JSONObjectUtils.getString(json, name)); 204 } else if (name.equals("crit")) { 205 h.setCriticalHeaders(new HashSet<String>(JSONObjectUtils.getStringList(json, name))); 206 } else if (name.equals("jku")) { 207 h.setJWKURL(JSONObjectUtils.getURL(json, name)); 208 } else if (name.equals("jwk")) { 209 h.setJWK(JWK.parse(JSONObjectUtils.getJSONObject(json, name))); 210 } else if (name.equals("x5u")) { 211 h.setX509CertURL(JSONObjectUtils.getURL(json, name)); 212 } else if (name.equals("x5t")) { 213 h.setX509CertThumbprint(new Base64URL(JSONObjectUtils.getString(json, name))); 214 } else if (name.equals("x5c")) { 215 h.setX509CertChain(CommonSEHeader.parseX509CertChain(JSONObjectUtils.getJSONArray(json, name))); 216 } else if (name.equals("kid")) { 217 h.setKeyID(JSONObjectUtils.getString(json, name)); 218 } else { 219 h.setCustomParameter(name, json.get(name)); 220 } 221 } 222 223 return h; 224 } 225 226 227 /** 228 * Parses a JWS header from the specified JSON string. 229 * 230 * @param s The JSON string to parse. Must not be {@code null}. 231 * 232 * @return The JWS header. 233 * 234 * @throws ParseException If the specified JSON object string doesn't 235 * represent a valid JWS header. 236 */ 237 public static JWSHeader parse(final String s) 238 throws ParseException { 239 240 JSONObject jsonObject = JSONObjectUtils.parseJSONObject(s); 241 242 return parse(jsonObject); 243 } 244 245 246 /** 247 * Parses a JWS header from the specified Base64URL. 248 * 249 * @param base64URL The Base64URL to parse. Must not be {@code null}. 250 * 251 * @return The JWS header. 252 * 253 * @throws ParseException If the specified Base64URL doesn't represent a 254 * valid JWS header. 255 */ 256 public static JWSHeader parse(final Base64URL base64URL) 257 throws ParseException { 258 259 JWSHeader header = parse(base64URL.decodeToString()); 260 header.setParsedBase64URL(base64URL); 261 return header; 262 } 263 }