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 Encryption (JWE) header.
021     *
022     * <p>Supports all {@link #getReservedParameterNames reserved header parameters}
023     * of the JWE specification:
024     *
025     * <ul>
026     *     <li>alg
027     *     <li>enc
028     *     <li>epk
029     *     <li>zip
030     *     <li>jku
031     *     <li>jwk
032     *     <li>x5u
033     *     <li>x5t
034     *     <li>x5c
035     *     <li>kid
036     *     <li>typ
037     *     <li>cty
038     *     <li>apu
039     *     <li>apv
040     *     <li>epu
041     *     <li>epv
042     * </ul>
043     *
044     * <p>The header may also carry {@link #setCustomParameters custom parameters};
045     * these will be serialised and parsed along the reserved ones.
046     *
047     * <p>Example header:
048     *
049     * <pre>
050     * { 
051     *   "alg" : "RSA1_5",
052     *   "enc" : "A128CBC+HS256"
053     * }
054     * </pre>
055     *
056     * @author Vladimir Dzhuvinov
057     * @version $version$ (2013-01-08)
058     */
059    public class JWEHeader extends CommonSEHeader implements ReadOnlyJWEHeader {
060    
061    
062            /**
063             * The reserved parameter names.
064             */
065            private static final Set<String> RESERVED_PARAMETER_NAMES;
066            
067            
068            /**
069             * Initialises the reserved parameter name set.
070             */
071            static {
072                    Set<String> p = new HashSet<String>();
073                    
074                    p.add("alg");
075                    p.add("enc");
076                    p.add("epk");
077                    p.add("zip");
078                    p.add("jku");
079                    p.add("jwk");
080                    p.add("x5u");
081                    p.add("x5t");
082                    p.add("x5c");
083                    p.add("kid");
084                    p.add("typ");
085                    p.add("cty");
086                    p.add("apu");
087                    p.add("apv");
088                    p.add("epu");
089                    p.add("epv");
090                    
091                    RESERVED_PARAMETER_NAMES = Collections.unmodifiableSet(p);
092            }
093            
094            
095            /**
096             * The encryption method ({@code enc}) parameter.
097             */
098            private EncryptionMethod enc;
099            
100            
101            /**
102             * The ephemeral public key ({@code epk}) parameter.
103             */
104            private ECKey epk;
105            
106            
107            /**
108             * The compression algorithm ({@code zip}) parameter.
109             */
110            private CompressionAlgorithm zip;
111            
112            
113            /**
114             * The agreement PartyUInfo ({@code apu}) parameter.
115             */
116            private Base64URL apu;
117    
118    
119            /**
120             * The agreement PartyVInfo ({@code apv}) parameter.
121             */
122            private Base64URL apv;
123            
124            
125            /**
126             * The encryption PartyUInfo ({@code epu}) parameter.
127             */
128            private Base64URL epu;
129            
130            
131            /**
132             * The encryption PartyUInfo ({@code epv}) parameter.
133             */
134            private Base64URL epv;
135            
136            
137            /**
138             * Creates a new JSON Web Encryption (JWE) header.
139             *
140             * @param alg The JWE algorithm parameter. Must not be {@code null}.
141             * @param enc The encryption method parameter. Must not be {@code null}.
142             */
143            public JWEHeader(final JWEAlgorithm alg, final EncryptionMethod enc) {
144            
145                    super(alg);
146                    
147                    if (enc == null)
148                            throw new IllegalArgumentException("The encryption method \"enc\" parameter must not be null");
149                    
150                    this.enc = enc;
151            }
152            
153            
154            /**
155             * Gets the reserved parameter names for JWE headers.
156             *
157             * @return The reserved parameter names, as an unmodifiable set.
158             */
159            public static Set<String> getReservedParameterNames() {
160            
161                    return RESERVED_PARAMETER_NAMES;
162            }
163            
164            
165            @Override
166            public JWEAlgorithm getAlgorithm() {
167            
168                    return (JWEAlgorithm)alg;
169            }
170            
171            
172            @Override
173            public EncryptionMethod getEncryptionMethod() {
174            
175                    return enc;
176            }
177            
178            
179            @Override
180            public ECKey getEphemeralPublicKey() {
181            
182                    return epk;
183            }
184            
185            
186            /**
187             * Sets the Ephemeral Public Key ({@code epk}) parameter.
188             *
189             * @param epk The Ephemeral Public Key parameter, {@code null} if not 
190             *            specified.
191             */
192            public void setEphemeralPublicKey(final ECKey epk) {
193            
194                    this.epk = epk;
195            }
196            
197            
198            @Override
199            public CompressionAlgorithm getCompressionAlgorithm() {
200            
201                    return zip;
202            }
203            
204            
205            /**
206             * Sets the compression algorithm ({@code zip}) parameter.
207             *
208             * @param zip The compression algorithm parameter, {@code null} if not 
209             *            specified.
210             */
211            public void setCompressionAlgorithm(final CompressionAlgorithm zip) {
212            
213                    this.zip = zip;
214            }
215            
216            
217            @Override
218            public Base64URL getAgreementPartyUInfo() {
219            
220                    return apu;
221            }
222            
223            
224            /**
225             * Sets the agreement PartyUInfo ({@code apu}) parameter.
226             *
227             * @param apu The agreement PartyUInfo parameter, {@code null} if not
228             *            specified.
229             */
230            public void setAgreementPartyUInfo(final Base64URL apu) {
231            
232                    this.apu = apu;
233            }
234            
235            
236            @Override
237            public Base64URL getAgreementPartyVInfo() {
238            
239                    return apv;
240            }
241            
242            
243            /**
244             * Sets the agreement PartyVInfo ({@code apv}) parameter.
245             *
246             * @param apv The agreement PartyVInfo parameter, {@code null} if not
247             *            specified.
248             */
249            public void setAgreementPartyVInfo(final Base64URL apv) {
250            
251                    this.apv = apv;
252            }
253            
254            
255            @Override
256            public Base64URL getEncryptionPartyUInfo() {
257            
258                    return epu;
259            }
260            
261            
262            /**
263             * Sets the encryption PartyUInfo ({@code epu}) parameter.
264             *
265             * @param epu The encryption PartyUInfo parameter, {@code null} if not
266             *            specified.
267             */
268            public void setEncryptionPartyUInfo(final Base64URL epu) {
269            
270                    this.epu = epu;
271            }
272            
273            
274            @Override
275            public Base64URL getEncryptionPartyVInfo() {
276            
277                    return epv;
278            }
279            
280            
281            /**
282             * Sets the encryption PartyVInfo ({@code epv}) parameter.
283             *
284             * @param epv The encryption PartyVInfo parameter, {@code null} if not
285             *            specified.
286             */
287            public void setEncryptionPartyVInfo(final Base64URL epv) {
288            
289                    this.epv = epv;
290            }
291            
292            
293            /**
294             * @throws IllegalArgumentException If the specified parameter name
295             *                                  matches a reserved parameter name.
296             */
297            @Override
298            public void setCustomParameter(final String name, final Object value) {
299            
300                    if (getReservedParameterNames().contains(name))
301                            throw new IllegalArgumentException("The parameter name \"" + name + "\" matches a reserved name");
302                    
303                    super.setCustomParameter(name, value);
304            }
305            
306            
307            @Override
308            public Set<String> getIncludedParameters() {
309            
310                    Set<String> includedParameters = 
311                            new HashSet<String>(getCustomParameters().keySet());
312                    
313                    includedParameters.add("alg");
314                    includedParameters.add("enc");
315                    
316                    if (getEphemeralPublicKey() != null)
317                            includedParameters.add("epk");
318                    
319                    if (getCompressionAlgorithm() != null)
320                            includedParameters.add("zip");
321                    
322                    if (getType() != null)
323                            includedParameters.add("typ");
324                            
325                    if (getContentType() != null)
326                            includedParameters.add("cty");
327                    
328                    if (getJWKURL() != null)
329                            includedParameters.add("jku");
330                    
331                    if (getJWK() != null)
332                            includedParameters.add("jwk");
333                    
334                    if (getX509CertURL() != null)
335                            includedParameters.add("x5u");
336                    
337                    if (getX509CertThumbprint() != null)
338                            includedParameters.add("x5t");
339                    
340                    if (getX509CertChain() != null)
341                            includedParameters.add("x5c");
342                    
343                    if (getKeyID() != null)
344                            includedParameters.add("kid");
345                    
346                    if (getAgreementPartyUInfo() != null)
347                            includedParameters.add("apu");
348                    
349                    if (getAgreementPartyVInfo() != null)
350                            includedParameters.add("apv");
351                    
352                    if (getEncryptionPartyUInfo() != null)
353                            includedParameters.add("epu");
354                    
355                    if (getEncryptionPartyVInfo() != null)
356                            includedParameters.add("epv");
357                    
358                    return includedParameters;
359            }
360            
361            
362            @Override
363            public JSONObject toJSONObject() {
364            
365                    JSONObject o = super.toJSONObject();
366            
367                    if (enc != null)
368                            o.put("enc", enc.toString());
369                    
370                    if (epk != null)
371                            o.put("epk", epk.toJSONObject());
372                    
373                    if (zip != null)
374                            o.put("zip", zip.toString());
375                    
376                    if (apu != null)
377                            o.put("apu", apu.toString());
378                    
379                    if (apv != null)
380                            o.put("apv", apv.toString());
381                    
382                    if (epu != null)
383                            o.put("epu", epu.toString());
384                    
385                    if (epv != null)
386                            o.put("epv", epv.toString());
387                    
388                    return o;
389            }
390            
391            
392            /**
393             * Parses an encryption method ({@code enc}) parameter from the 
394             * specified JWE header JSON object.
395             *
396             * @param json The JSON object to parse. Must not be {@code null}.
397             *
398             * @return The encryption method.
399             *
400             * @throws ParseException If the {@code enc} parameter couldn't be 
401             *                        parsed.
402             */
403            private static EncryptionMethod parseEncryptionMethod(final JSONObject json)
404                    throws ParseException {
405                    
406                    return EncryptionMethod.parse(JSONObjectUtils.getString(json, "enc"));
407            }
408            
409            
410            /**
411             * Parses a JWE header from the specified JSON object.
412             *
413             * @param json The JSON object to parse. Must not be {@code null}.
414             *
415             * @return The JWE header.
416             *
417             * @throws ParseException If the specified JSON object doesn't 
418             *                        represent a valid JWE header.
419             */
420            public static JWEHeader parse(final JSONObject json)
421                    throws ParseException {
422            
423                    // Get the "alg" parameter
424                    Algorithm alg = Header.parseAlgorithm(json);
425                    
426                    if (! (alg instanceof JWEAlgorithm))
427                            throw new ParseException("The algorithm \"alg\" header parameter must be for encryption", 0);
428                    
429                    // Get the "enc" parameter
430                    EncryptionMethod enc = parseEncryptionMethod(json);
431                    
432                    // Create a minimal header
433                    JWEHeader h = new JWEHeader((JWEAlgorithm)alg, enc);
434            
435                    // Parse optional + custom parameters
436                    for(final String name: json.keySet()) {
437                            
438                            if (name.equals("alg")) 
439                                    continue; // skip
440                            
441                            else if (name.equals("enc")) 
442                                    continue; // skip
443                            
444                            else if (name.equals("epk")) 
445                                    h.setEphemeralPublicKey(ECKey.parse(JSONObjectUtils.getJSONObject(json, name)));
446                            
447                            else if (name.equals("zip")) 
448                                    h.setCompressionAlgorithm(new CompressionAlgorithm(JSONObjectUtils.getString(json, name)));
449                            
450                            else if (name.equals("typ")) 
451                                    h.setType(new JOSEObjectType(JSONObjectUtils.getString(json, name)));
452                            
453                            else if (name.equals("cty")) 
454                                    h.setContentType(JSONObjectUtils.getString(json, name));
455                            
456                            else if (name.equals("jku")) 
457                                    h.setJWKURL(JSONObjectUtils.getURL(json, name));
458                            
459                            else if (name.equals("jwk")) 
460                                    h.setJWK(JWK.parse(JSONObjectUtils.getJSONObject(json, name)));
461                            
462                            else if (name.equals("x5u")) 
463                                    h.setX509CertURL(JSONObjectUtils.getURL(json, name));
464                            
465                            else if (name.equals("x5t"))
466                                    h.setX509CertThumbprint(new Base64URL(JSONObjectUtils.getString(json, name)));
467                            
468                            else if (name.equals("x5c")) 
469                                    h.setX509CertChain(CommonSEHeader.parseX509CertChain(JSONObjectUtils.getJSONArray(json, name)));
470                            
471                            else if (name.equals("kid"))
472                                    h.setKeyID(JSONObjectUtils.getString(json, name));
473                            
474                            else if (name.equals("apu"))
475                                    h.setAgreementPartyUInfo(new Base64URL(JSONObjectUtils.getString(json, name)));
476                            
477                            else if (name.equals("apv"))
478                                    h.setAgreementPartyVInfo(new Base64URL(JSONObjectUtils.getString(json, name)));
479                            
480                            else if (name.equals("epu"))
481                                    h.setEncryptionPartyUInfo(new Base64URL(JSONObjectUtils.getString(json, name)));
482                            
483                            else if (name.equals("epv"))
484                                    h.setEncryptionPartyVInfo(new Base64URL(JSONObjectUtils.getString(json, name)));
485                            
486                            else
487                                    h.setCustomParameter(name, json.get(name));
488                            
489                    }
490                    
491                    return h;
492            }
493            
494            
495            /**
496             * Parses a JWE header from the specified JSON string.
497             *
498             * @param s The JSON string to parse. Must not be {@code null}.
499             *
500             * @return The JWE header.
501             *
502             * @throws ParseException If the specified JSON object string doesn't 
503             *                        represent a valid JWE header.
504             */
505            public static JWEHeader parse(final String s)
506                    throws ParseException {
507                    
508                    JSONObject jsonObject = JSONObjectUtils.parseJSONObject(s);
509                    
510                    return parse(jsonObject);
511            }
512            
513            
514            /**
515             * Parses a JWE header from the specified Base64URL.
516             *
517             * @param base64URL The Base64URL to parse. Must not be {@code null}.
518             *
519             * @return The JWE header.
520             *
521             * @throws ParseException If the specified Base64URL doesn't represent a 
522             *                        valid JWE header.
523             */
524            public static JWEHeader parse(final Base64URL base64URL)
525                    throws ParseException {
526                            
527                    return parse(base64URL.decodeToString());
528            }
529    }