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