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}