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