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