001 package com.nimbusds.jose; 002 003 004 import java.text.ParseException; 005 import java.util.Collections; 006 import java.util.HashMap; 007 import java.util.Map; 008 009 import net.minidev.json.JSONObject; 010 011 import com.nimbusds.jose.util.Base64URL; 012 import com.nimbusds.jose.util.JSONObjectUtils; 013 014 015 /** 016 * The base abstract class for plaintext, JSON Web Signature (JWS) and JSON Web 017 * Encryption (JWE) headers. 018 * 019 * <p>The header may also carry {@link #setCustomParameters custom parameters}; 020 * these will be serialised and parsed along the reserved ones. 021 * 022 * @author Vladimir Dzhuvinov 023 * @version $version$ (2012-12-09) 024 */ 025 public abstract class Header implements ReadOnlyHeader { 026 027 028 /** 029 * The algorithm ({@code alg}) parameter. 030 */ 031 final protected Algorithm alg; 032 033 034 /** 035 * The JOSE object type ({@code typ}) parameter. 036 */ 037 private JOSEObjectType typ; 038 039 040 /** 041 * The content type ({@code cty}) parameter. 042 */ 043 private String cty; 044 045 046 /** 047 * Custom header parameters. 048 */ 049 private Map<String,Object> customParameters = new HashMap<String,Object>(); 050 051 052 /** 053 * Creates a new header with the specified algorithm ({@code alg}) 054 * parameter. 055 * 056 * @param alg The algorithm parameter. Must not be {@code null}. 057 */ 058 protected Header(final Algorithm alg) { 059 060 if (alg == null) { 061 throw new IllegalArgumentException("The algorithm \"alg\" header parameter must not be null"); 062 } 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 147 this.customParameters = customParameters; 148 } 149 150 151 @Override 152 public JSONObject toJSONObject() { 153 154 // Include custom parameters, they will be overwritten if their 155 // names match specified reserved ones 156 JSONObject o = new JSONObject(customParameters); 157 158 // Alg is always defined 159 o.put("alg", alg.toString()); 160 161 if (typ != null) { 162 o.put("typ", typ.toString()); 163 } 164 165 if (cty != null) { 166 o.put("cty", cty); 167 } 168 169 return o; 170 } 171 172 173 /** 174 * Returns a JSON string representation of this header. All custom 175 * parameters will be included if they serialise to a JSON entity and 176 * their names don't conflict with the reserved ones. 177 * 178 * @return The JSON string representation of this header. 179 */ 180 @Override 181 public String toString() { 182 183 return toJSONObject().toString(); 184 } 185 186 187 @Override 188 public Base64URL toBase64URL() { 189 190 return Base64URL.encode(toString()); 191 } 192 193 194 /** 195 * Parses an algorithm ({@code alg}) parameter from the specified 196 * header JSON object. Intended for initial parsing of plain, JWS and 197 * JWE headers. 198 * 199 * <p>The algorithm type (none, JWS or JWE) is determined by inspecting 200 * the algorithm name for "none" and the presence of an "enc" parameter. 201 * 202 * @param json The JSON object to parse. Must not be {@code null}. 203 * 204 * @return The algorithm, an instance of {@link Algorithm#NONE}, 205 * {@link JWSAlgorithm} or {@link JWEAlgorithm}. 206 * 207 * @throws ParseException If the {@code alg} parameter couldn't be 208 * parsed. 209 */ 210 public static Algorithm parseAlgorithm(final JSONObject json) 211 throws ParseException { 212 213 String algName = JSONObjectUtils.getString(json, "alg"); 214 215 // Infer algorithm type 216 217 if (algName.equals(Algorithm.NONE.getName())) { 218 // Plain 219 return Algorithm.NONE; 220 } else if (json.containsKey("enc")) { 221 // JWE 222 return JWEAlgorithm.parse(algName); 223 } else { 224 // JWS 225 return JWSAlgorithm.parse(algName); 226 } 227 } 228 229 230 /** 231 * Parses a {@link PlainHeader}, {@link JWSHeader} or {@link JWEHeader} 232 * from the specified JSON object. 233 * 234 * @param json The JSON object to parse. Must not be {@code null}. 235 * 236 * @return The header. 237 * 238 * @throws ParseException If the specified JSON object doesn't represent 239 * a valid header. 240 */ 241 public static Header parse(final JSONObject json) 242 throws ParseException { 243 244 Algorithm alg = parseAlgorithm(json); 245 246 if (alg.equals(Algorithm.NONE)) { 247 return PlainHeader.parse(json); 248 } else if (alg instanceof JWSAlgorithm) { 249 return JWSHeader.parse(json); 250 } else if (alg instanceof JWEAlgorithm) { 251 return JWEHeader.parse(json); 252 } else { 253 throw new AssertionError("Unexpected algorithm type: " + alg); 254 } 255 } 256 }