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