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 #getReservedParameterNames reserved header parameters}
019 * 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 reserved ones.
030 *
031 * <p>Example:
032 *
033 * <pre>
034 * {
035 *   "alg" : "none"
036 * }
037 * </pre>
038 *
039 * @author Vladimir Dzhuvinov
040 * @version $version$ (2013-05-07)
041 */
042public class PlainHeader extends Header implements ReadOnlyPlainHeader {
043
044
045        /**
046         * The reserved parameter names.
047         */
048        private static final Set<String> RESERVED_PARAMETER_NAMES;
049
050
051        /**
052         * Initialises the reserved 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                RESERVED_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 reserved parameter names for plain headers.
078         *
079         * @return The reserved parameter names, as an unmodifiable set.
080         */
081        public static Set<String> getReservedParameterNames() {
082
083                return RESERVED_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 reserved parameter name.
097         */
098        @Override
099        public void setCustomParameter(final String name, final Object value) {
100
101                if (getReservedParameterNames().contains(name)) {
102                        throw new IllegalArgumentException("The parameter name \"" + name + "\" matches a reserved name");
103                }
104
105                super.setCustomParameter(name, value);
106        }
107
108
109        @Override
110        public Set<String> getIncludedParameters() {
111
112                Set<String> includedParameters = 
113                        new HashSet<String>(getCustomParameters().keySet());
114
115                includedParameters.add("alg");
116
117                if (getType() != null) {
118                        includedParameters.add("typ");
119                }
120
121                if (getContentType() != null) {
122                        includedParameters.add("cty");
123                }
124
125                if (getCriticalHeaders() != null && ! getCriticalHeaders().isEmpty()) {
126                        includedParameters.add("crit");
127                }
128
129                return includedParameters;
130        }
131
132
133        /**
134         * Parses a plain header from the specified JSON object.
135         *
136         * @param json The JSON object to parse. Must not be {@code null}.
137         *
138         * @return The plain header.
139         *
140         * @throws ParseException If the specified JSON object doesn't represent
141         *                        a valid plain header.
142         */
143        public static PlainHeader parse(final JSONObject json)
144                throws ParseException {
145
146                // Get the "alg" parameter
147                Algorithm alg = Header.parseAlgorithm(json);
148
149                if (alg != Algorithm.NONE) {
150                        throw new ParseException("The algorithm \"alg\" header parameter must be \"none\"", 0);
151                }
152
153
154                // Create a minimal header, type may be set later
155                PlainHeader h = new PlainHeader();
156
157
158                // Parse optional + custom parameters
159                for(final String name: json.keySet()) {
160
161                        if (name.equals("alg")) {
162                                continue; // skip
163                        } else if (name.equals("typ")) {
164                                h.setType(new JOSEObjectType(JSONObjectUtils.getString(json, name)));
165                        } else if (name.equals("cty")) {
166                                h.setContentType(JSONObjectUtils.getString(json, name));
167                        } else if (name.equals("crit")) {
168                                h.setCriticalHeaders(new HashSet<String>(JSONObjectUtils.getStringList(json, name)));
169                        } else {
170                                h.setCustomParameter(name, json.get(name));
171                        }
172                }
173
174                return h;
175        }
176
177
178        /**
179         * Parses a plain header from the specified JSON string.
180         *
181         * @param s The JSON string to parse. Must not be {@code null}.
182         *
183         * @return The plain header.
184         *
185         * @throws ParseException If the specified JSON string doesn't 
186         *                        represent a valid plain header.
187         */
188        public static PlainHeader parse(final String s)
189                throws ParseException {
190
191                JSONObject jsonObject = JSONObjectUtils.parseJSONObject(s);
192
193                return parse(jsonObject);
194        }
195
196
197        /**
198         * Parses a plain header from the specified Base64URL.
199         *
200         * @param base64URL The Base64URL to parse. Must not be {@code null}.
201         *
202         * @return The plain header.
203         *
204         * @throws ParseException If the specified Base64URL doesn't represent a
205         *                        valid plain header.
206         */
207        public static PlainHeader parse(final Base64URL base64URL)
208                throws ParseException {
209
210                if (base64URL == null) {
211                        throw new ParseException("The Base64URL must not be null", 0);
212                }
213
214                PlainHeader header = parse(base64URL.decodeToString());
215                header.setParsedBase64URL(base64URL);
216                return header;
217        }
218}