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 }