001    package com.nimbusds.jose;
002    
003    
004    import java.net.URL;
005    
006    import java.text.ParseException;
007    
008    import java.util.Collections;
009    import java.util.HashSet;
010    import java.util.Set;
011    
012    import net.minidev.json.JSONArray;
013    import net.minidev.json.JSONObject;
014    
015    import com.nimbusds.jose.util.Base64URL;
016    import com.nimbusds.jose.util.JSONObjectUtils;
017    
018    
019    /**
020     * JSON Web Signature (JWS) header.
021     *
022     * <p>Supports all {@link #getReservedParameterNames reserved header parameters}
023     * of the JWS specification:
024     *
025     * <ul>
026     *     <li>alg
027     *     <li>jku
028     *     <li>jwk
029     *     <li>x5u
030     *     <li>x5t
031     *     <li>x5c
032     *     <li>kid
033     *     <li>typ
034     *     <li>cty
035     * </ul>
036     *
037     * <p>The header may also carry {@link #setCustomParameters custom parameters};
038     * these will be serialised and parsed along the reserved ones.
039     *
040     * <p>Example header of a JSON Web Signature (JWS) object using the 
041     * {@link JWSAlgorithm#HS256 HMAC SHA-256 algorithm}:
042     *
043     * <pre>
044     * {
045     *   "alg" : "HS256"
046     * }
047     * </pre>
048     *
049     * @author Vladimir Dzhuvinov
050     * @version $version$ (2013-01-08)
051     */
052    public class JWSHeader extends CommonSEHeader implements ReadOnlyJWSHeader {
053    
054    
055            /**
056             * The reserved parameter names.
057             */
058            private static final Set<String> RESERVED_PARAMETER_NAMES;
059            
060            
061            /**
062             * Initialises the reserved parameter name set.
063             */
064            static {
065                    Set<String> p = new HashSet<String>();
066                    
067                    p.add("alg");
068                    p.add("jku");
069                    p.add("jwk");
070                    p.add("x5u");
071                    p.add("x5t");
072                    p.add("x5c");
073                    p.add("kid");
074                    p.add("typ");
075                    p.add("cty");
076                    
077                    RESERVED_PARAMETER_NAMES = Collections.unmodifiableSet(p);
078            }
079            
080            
081            /**
082             * Creates a new JSON Web Signature (JWS) header.
083             *
084             * @param alg The JWS algorithm. Must not be {@code null}.
085             */
086            public JWSHeader(final JWSAlgorithm alg) {
087            
088                    super(alg);
089            }
090            
091            
092            /**
093             * Gets the reserved parameter names for JWS headers.
094             *
095             * @return The reserved parameter names, as an unmodifiable set.
096             */
097            public static Set<String> getReservedParameterNames() {
098            
099                    return RESERVED_PARAMETER_NAMES;
100            }
101            
102            
103            @Override
104            public JWSAlgorithm getAlgorithm() {
105            
106                    return (JWSAlgorithm)alg;
107            }
108            
109            
110            /**
111             * @throws IllegalArgumentException If the specified parameter name
112             *                                  matches a reserved parameter name.
113             */
114            @Override
115            public void setCustomParameter(final String name, final Object value) {
116            
117                    if (getReservedParameterNames().contains(name))
118                            throw new IllegalArgumentException("The parameter name \"" + name + "\" matches a reserved name");
119                    
120                    super.setCustomParameter(name, value);
121            }
122            
123            
124            @Override
125            public Set<String> getIncludedParameters() {
126            
127                    Set<String> includedParameters = 
128                            new HashSet<String>(getCustomParameters().keySet());
129                    
130                    includedParameters.add("alg");
131                    
132                    if (getType() != null)
133                            includedParameters.add("typ");
134                            
135                    if (getContentType() != null)
136                            includedParameters.add("cty");
137                    
138                    if (getJWKURL() != null)
139                            includedParameters.add("jku");
140                    
141                    if (getJWK() != null)
142                            includedParameters.add("jwk");
143                    
144                    if (getX509CertURL() != null)
145                            includedParameters.add("x5u");
146                    
147                    if (getX509CertThumbprint() != null)
148                            includedParameters.add("x5t");
149                    
150                    if (getX509CertChain() != null)
151                            includedParameters.add("x5c");
152                    
153                    if (getKeyID() != null)
154                            includedParameters.add("kid");
155                    
156                    return includedParameters;
157            }
158            
159            
160            /**
161             * Parses a JWS header from the specified JSON object.
162             *
163             * @param json The JSON object to parse. Must not be {@code null}.
164             *
165             * @return The JWS header.
166             *
167             * @throws ParseException If the specified JSON object doesn't 
168             *                        represent a valid JWS header.
169             */
170            public static JWSHeader parse(final JSONObject json)
171                    throws ParseException {
172            
173                    // Get the "alg" parameter
174                    Algorithm alg = Header.parseAlgorithm(json);
175                    
176                    if (! (alg instanceof JWSAlgorithm))
177                            throw new ParseException("The algorithm \"alg\" header parameter must be for signatures", 0);
178                    
179                    // Create a minimal header
180                    JWSHeader h = new JWSHeader((JWSAlgorithm)alg);
181                    
182                    // Parse optional + custom parameters
183                    for (final String name: json.keySet()) {
184                            
185                            if (name.equals("alg"))
186                                    continue; // Skip
187                            
188                            else if (name.equals("typ"))
189                                    h.setType(new JOSEObjectType(JSONObjectUtils.getString(json, name)));
190                            
191                            else if (name.equals("cty"))
192                                    h.setContentType(JSONObjectUtils.getString(json, name));
193                            
194                            else if (name.equals("jku"))
195                                    h.setJWKURL(JSONObjectUtils.getURL(json, name));
196                            
197                            else if (name.equals("jwk"))
198                                    h.setJWK(JWK.parse(JSONObjectUtils.getJSONObject(json, name)));
199                            
200                            else if (name.equals("x5u"))
201                                    h.setX509CertURL(JSONObjectUtils.getURL(json, name));
202                            
203                            else if (name.equals("x5t"))
204                                    h.setX509CertThumbprint(new Base64URL(JSONObjectUtils.getString(json, name)));
205                            
206                            else if (name.equals("x5c"))
207                                    h.setX509CertChain(CommonSEHeader.parseX509CertChain(JSONObjectUtils.getJSONArray(json, name)));
208                            
209                            else if (name.equals("kid"))
210                                    h.setKeyID(JSONObjectUtils.getString(json, name));
211                            
212                            else
213                                    h.setCustomParameter(name, json.get(name));
214                    }
215                    
216                    return h;
217            }
218            
219            
220            /**
221             * Parses a JWS header from the specified JSON string.
222             *
223             * @param s The JSON string to parse. Must not be {@code null}.
224             *
225             * @return The JWS header.
226             *
227             * @throws ParseException If the specified JSON object string doesn't 
228             *                        represent a valid JWS header.
229             */
230            public static JWSHeader parse(final String s)
231                    throws ParseException {
232                    
233                    JSONObject jsonObject = JSONObjectUtils.parseJSONObject(s);
234                    
235                    return parse(jsonObject);
236            }
237            
238            
239            /**
240             * Parses a JWS header from the specified Base64URL.
241             *
242             * @param base64URL The Base64URL to parse. Must not be {@code null}.
243             *
244             * @return The JWS header.
245             *
246             * @throws ParseException If the specified Base64URL doesn't represent a 
247             *                        valid JWS header.
248             */
249            public static JWSHeader parse(final Base64URL base64URL)
250                    throws ParseException {
251                            
252                    return parse(base64URL.decodeToString());
253            }
254    }