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