001package com.nimbusds.jose;
002
003
004import java.text.ParseException;
005import java.util.Collections;
006import java.util.HashMap;
007import java.util.Map;
008
009import net.minidev.json.JSONObject;
010
011import com.nimbusds.jose.util.Base64URL;
012import 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 */
025public 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}