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.util.Base64URL;
012import com.nimbusds.jose.util.JSONObjectUtils;
013
014
015/**
016 * Plaintext JOSE header.
017 *
018 * <p>Supports all {@link #getRegisteredParameterNames registered header
019 * parameters} of the plain specification:
020 *
021 * <ul>
022 *     <li>alg (set to {@link Algorithm#NONE "none"}).
023 *     <li>typ
024 *     <li>cty
025 *     <li>crit
026 * </ul>
027 *
028 * <p>The header may also carry {@link #setCustomParameters custom parameters};
029 * these will be serialised and parsed along the registered ones.
030 *
031 * <p>Example:
032 *
033 * <pre>
034 * {
035 *   "alg" : "none"
036 * }
037 * </pre>
038 *
039 * @author Vladimir Dzhuvinov
040 * @version $version$ (2013-10-07)
041 */
042public class PlainHeader extends Header implements ReadOnlyPlainHeader {
043
044
045        /**
046         * The registered parameter names.
047         */
048        private static final Set<String> REGISTERED_PARAMETER_NAMES;
049
050
051        /**
052         * Initialises the registered parameter name set.
053         */
054        static {
055                Set<String> p = new HashSet<String>();
056
057                p.add("alg");
058                p.add("typ");
059                p.add("cty");
060                p.add("crit");
061
062                REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p);
063        }
064
065
066        /**
067         * Creates a new plain header with algorithm 
068         * {@link Algorithm#NONE none}.
069         */
070        public PlainHeader() {
071
072                super(Algorithm.NONE);
073        }
074
075
076        /**
077         * Gets the registered parameter names for plain headers.
078         *
079         * @return The registered parameter names, as an unmodifiable set.
080         */
081        public static Set<String> getRegisteredParameterNames() {
082
083                return REGISTERED_PARAMETER_NAMES;
084        }
085
086
087        @Override
088        public Algorithm getAlgorithm() {
089
090                return alg;
091        }
092
093
094        /**
095         * @throws IllegalArgumentException If the specified parameter name
096         *                                  matches a registered parameter
097         *                                  name.
098         */
099        @Override
100        public void setCustomParameter(final String name, final Object value) {
101
102                if (getRegisteredParameterNames().contains(name)) {
103                        throw new IllegalArgumentException("The parameter name \"" + name + "\" matches a registered name");
104                }
105
106                super.setCustomParameter(name, value);
107        }
108
109
110        @Override
111        public Set<String> getIncludedParameters() {
112
113                Set<String> includedParameters = 
114                        new HashSet<String>(getCustomParameters().keySet());
115
116                includedParameters.add("alg");
117
118                if (getType() != null) {
119                        includedParameters.add("typ");
120                }
121
122                if (getContentType() != null) {
123                        includedParameters.add("cty");
124                }
125
126                if (getCriticalHeaders() != null && ! getCriticalHeaders().isEmpty()) {
127                        includedParameters.add("crit");
128                }
129
130                return includedParameters;
131        }
132
133
134        /**
135         * Parses a plain header from the specified JSON object.
136         *
137         * @param json The JSON object to parse. Must not be {@code null}.
138         *
139         * @return The plain header.
140         *
141         * @throws ParseException If the specified JSON object doesn't
142         *                        represent a valid plain header.
143         */
144        public static PlainHeader parse(final JSONObject json)
145                throws ParseException {
146
147                // Get the "alg" parameter
148                Algorithm alg = Header.parseAlgorithm(json);
149
150                if (alg != Algorithm.NONE) {
151                        throw new ParseException("The algorithm \"alg\" header parameter must be \"none\"", 0);
152                }
153
154
155                // Create a minimal header, type may be set later
156                PlainHeader h = new PlainHeader();
157
158
159                // Parse optional + custom parameters
160                for(final String name: json.keySet()) {
161
162                        if (name.equals("alg")) {
163                                continue; // skip
164                        } else if (name.equals("typ")) {
165                                h.setType(new JOSEObjectType(JSONObjectUtils.getString(json, name)));
166                        } else if (name.equals("cty")) {
167                                h.setContentType(JSONObjectUtils.getString(json, name));
168                        } else if (name.equals("crit")) {
169                                h.setCriticalHeaders(new HashSet<String>(JSONObjectUtils.getStringList(json, name)));
170                        } else {
171                                h.setCustomParameter(name, json.get(name));
172                        }
173                }
174
175                return h;
176        }
177
178
179        /**
180         * Parses a plain header from the specified JSON string.
181         *
182         * @param s The JSON string to parse. Must not be {@code null}.
183         *
184         * @return The plain header.
185         *
186         * @throws ParseException If the specified JSON string doesn't 
187         *                        represent a valid plain header.
188         */
189        public static PlainHeader parse(final String s)
190                throws ParseException {
191
192                JSONObject jsonObject = JSONObjectUtils.parseJSONObject(s);
193
194                return parse(jsonObject);
195        }
196
197
198        /**
199         * Parses a plain header from the specified Base64URL.
200         *
201         * @param base64URL The Base64URL to parse. Must not be {@code null}.
202         *
203         * @return The plain header.
204         *
205         * @throws ParseException If the specified Base64URL doesn't represent
206         *                        a valid plain header.
207         */
208        public static PlainHeader parse(final Base64URL base64URL)
209                throws ParseException {
210
211                if (base64URL == null) {
212                        throw new ParseException("The Base64URL must not be null", 0);
213                }
214
215                PlainHeader header = parse(base64URL.decodeToString());
216                header.setParsedBase64URL(base64URL);
217                return header;
218        }
219}