001 package com.nimbusds.jose; 002 003 004 import java.text.ParseException; 005 006 import java.util.Collections; 007 import java.util.HashMap; 008 import java.util.Map; 009 010 import net.minidev.json.JSONObject; 011 012 import com.nimbusds.jose.util.Base64URL; 013 import com.nimbusds.jose.util.JSONObjectUtils; 014 015 016 /** 017 * The base abstract class for plaintext, JSON Web Signature (JWS) and JSON Web 018 * Encryption (JWE) headers. 019 * 020 * <p>The header may also carry {@link #setCustomParameters custom parameters}; 021 * these will be serialised and parsed along the reserved ones. 022 * 023 * @author Vladimir Dzhuvinov 024 * @version $version$ (2012-12-09) 025 */ 026 public abstract class Header implements ReadOnlyHeader { 027 028 029 /** 030 * The algorithm ({@code alg}) parameter. 031 */ 032 final protected Algorithm alg; 033 034 035 /** 036 * The JOSE object type ({@code typ}) parameter. 037 */ 038 private JOSEObjectType typ; 039 040 041 /** 042 * The content type ({@code cty}) parameter. 043 */ 044 private String cty; 045 046 047 /** 048 * Custom header parameters. 049 */ 050 private Map<String,Object> customParameters = new HashMap<String,Object>(); 051 052 053 /** 054 * Creates a new header with the specified algorithm ({@code alg}) 055 * parameter. 056 * 057 * @param alg The algorithm parameter. Must not be {@code null}. 058 */ 059 protected Header(final Algorithm alg) { 060 061 if (alg == null) 062 throw new IllegalArgumentException("The algorithm \"alg\" header parameter must not be null"); 063 064 this.alg = alg; 065 } 066 067 068 @Override 069 public JOSEObjectType getType() { 070 071 return typ; 072 } 073 074 075 /** 076 * Sets the type ({@code typ}) parameter. 077 * 078 * @param typ The type parameter, {@code null} if not specified. 079 */ 080 public void setType(final JOSEObjectType typ) { 081 082 this.typ = typ; 083 } 084 085 086 @Override 087 public String getContentType() { 088 089 return cty; 090 } 091 092 093 /** 094 * Sets the content type ({@code cty}) parameter. 095 * 096 * @param cty The content type parameter, {@code null} if not specified. 097 */ 098 public void setContentType(final String cty) { 099 100 this.cty = cty; 101 } 102 103 104 @Override 105 public Object getCustomParameter(final String name) { 106 107 return customParameters.get(name); 108 } 109 110 111 /** 112 * Sets a custom (non-reserved) parameter. Callers and extending classes 113 * should ensure the parameter name doesn't match a reserved parameter 114 * name. 115 * 116 * @param name The name of the custom parameter. Must not match a 117 * reserved parameter name and must not be {@code null}. 118 * @param value The value of the custom parameter, should map to a valid 119 * JSON entity, {@code null} if not specified. 120 */ 121 protected void setCustomParameter(final String name, final Object value) { 122 123 customParameters.put(name, value); 124 } 125 126 127 @Override 128 public Map<String,Object> getCustomParameters() { 129 130 return Collections.unmodifiableMap(customParameters); 131 } 132 133 134 /** 135 * Sets the custom (non-reserved) parameters. The values must be 136 * serialisable to a JSON entity, otherwise will be ignored. 137 * 138 * @param customParameters The custom parameters, empty map or 139 * {@code null} if none. 140 */ 141 public void setCustomParameters(final Map<String,Object> customParameters) { 142 143 if (customParameters == null) 144 return; 145 146 this.customParameters = customParameters; 147 } 148 149 150 @Override 151 public JSONObject toJSONObject() { 152 153 // Include custom parameters, they will be overwritten if their 154 // names match specified reserved ones 155 JSONObject o = new JSONObject(customParameters); 156 157 // Alg is always defined 158 o.put("alg", alg.toString()); 159 160 if (typ != null) 161 o.put("typ", typ.toString()); 162 163 if (cty != null) 164 o.put("cty", cty); 165 166 return o; 167 } 168 169 170 /** 171 * Returns a JSON string representation of this header. All custom 172 * parameters will be included if they serialise to a JSON entity and 173 * their names don't conflict with the reserved ones. 174 * 175 * @return The JSON string representation of this header. 176 */ 177 public String toString() { 178 179 return toJSONObject().toString(); 180 } 181 182 183 @Override 184 public Base64URL toBase64URL() { 185 186 return Base64URL.encode(toString()); 187 } 188 189 190 /** 191 * Parses an algorithm ({@code alg}) parameter from the specified 192 * header JSON object. Intended for initial parsing of plain, JWS and 193 * JWE headers. 194 * 195 * <p>The algorithm type (none, JWS or JWE) is determined by inspecting 196 * the algorithm name for "none" and the presence of an "enc" parameter. 197 * 198 * @param json The JSON object to parse. Must not be {@code null}. 199 * 200 * @return The algorithm, an instance of {@link Algorithm#NONE}, 201 * {@link JWSAlgorithm} or {@link JWEAlgorithm}. 202 * 203 * @throws ParseException If the {@code alg} parameter couldn't be 204 * parsed. 205 */ 206 public static Algorithm parseAlgorithm(final JSONObject json) 207 throws ParseException { 208 209 String algName = JSONObjectUtils.getString(json, "alg"); 210 211 // Infer algorithm type 212 213 // Plain 214 if (algName.equals(Algorithm.NONE.getName())) 215 return Algorithm.NONE; 216 217 // JWE 218 else if (json.containsKey("enc")) 219 return JWEAlgorithm.parse(algName); 220 221 // JWS 222 else 223 return JWSAlgorithm.parse(algName); 224 } 225 226 227 /** 228 * Parses a {@link PlainHeader}, {@link JWSHeader} or {@link JWEHeader} 229 * from the specified JSON object. 230 * 231 * @param json The JSON object to parse. Must not be {@code null}. 232 * 233 * @return The header. 234 * 235 * @throws ParseException If the specified JSON object doesn't represent 236 * a valid header. 237 */ 238 public static Header parse(final JSONObject json) 239 throws ParseException { 240 241 Algorithm alg = parseAlgorithm(json); 242 243 if (alg.equals(Algorithm.NONE)) 244 return PlainHeader.parse(json); 245 246 else if (alg instanceof JWSAlgorithm) 247 return JWSHeader.parse(json); 248 249 else if (alg instanceof JWEAlgorithm) 250 return JWEHeader.parse(json); 251 252 else 253 throw new AssertionError("Unexpected algorithm type: " + alg); 254 } 255 }