001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose; 019 020 021import java.text.ParseException; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.Map; 026import java.util.Set; 027 028import net.jcip.annotations.Immutable; 029 030import net.minidev.json.JSONObject; 031 032import com.nimbusds.jose.util.Base64URL; 033import com.nimbusds.jose.util.JSONObjectUtils; 034 035 036/** 037 * Unsecured ({@code alg=none}) JOSE header. This class is immutable. 038 * 039 * <p>Supports all {@link #getRegisteredParameterNames registered header 040 * parameters} of the unsecured JOSE object specification: 041 * 042 * <ul> 043 * <li>alg (set to {@link Algorithm#NONE "none"}). 044 * <li>typ 045 * <li>cty 046 * <li>crit 047 * </ul> 048 * 049 * <p>The header may also carry {@link #getCustomParams custom parameters}; 050 * these will be serialised and parsed along the registered ones. 051 * 052 * <p>Example: 053 * 054 * <pre> 055 * { 056 * "alg" : "none" 057 * } 058 * </pre> 059 * 060 * @author Vladimir Dzhuvinov 061 * @version 2014-08-20 062 */ 063@Immutable 064public final class PlainHeader extends Header { 065 066 067 private static final long serialVersionUID = 1L; 068 069 070 /** 071 * The registered parameter names. 072 */ 073 private static final Set<String> REGISTERED_PARAMETER_NAMES; 074 075 076 /** 077 * Initialises the registered parameter name set. 078 */ 079 static { 080 Set<String> p = new HashSet<>(); 081 082 p.add("alg"); 083 p.add("typ"); 084 p.add("cty"); 085 p.add("crit"); 086 087 REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p); 088 } 089 090 091 /** 092 * Builder for constructing unsecured (plain) headers. 093 * 094 * <p>Example usage: 095 * 096 * <pre> 097 * PlainHeader header = new PlainHeader.Builder(). 098 * contentType("text/plain"). 099 * customParam("exp", new Date().getTime()). 100 * build(); 101 * </pre> 102 */ 103 public static class Builder { 104 105 106 /** 107 * The JOSE object type. 108 */ 109 private JOSEObjectType typ; 110 111 112 /** 113 * The content type. 114 */ 115 private String cty; 116 117 118 /** 119 * The critical headers. 120 */ 121 private Set<String> crit; 122 123 124 /** 125 * Custom header parameters. 126 */ 127 private Map<String,Object> customParams; 128 129 130 /** 131 * The parsed Base64URL. 132 */ 133 private Base64URL parsedBase64URL; 134 135 136 /** 137 * Creates a new unsecured (plain) header builder. 138 */ 139 public Builder() { 140 141 } 142 143 144 /** 145 * Creates a new unsecured (plain) header builder with the 146 * parameters from the specified header. 147 * 148 * @param plainHeader The unsecured header to use. Must not be 149 * {@code null}. 150 */ 151 public Builder(final PlainHeader plainHeader) { 152 153 typ = plainHeader.getType(); 154 cty = plainHeader.getContentType(); 155 crit = plainHeader.getCriticalParams(); 156 customParams = plainHeader.getCustomParams(); 157 } 158 159 160 /** 161 * Sets the type ({@code typ}) parameter. 162 * 163 * @param typ The type parameter, {@code null} if not 164 * specified. 165 * 166 * @return This builder. 167 */ 168 public Builder type(final JOSEObjectType typ) { 169 170 this.typ = typ; 171 return this; 172 } 173 174 175 /** 176 * Sets the content type ({@code cty}) parameter. 177 * 178 * @param cty The content type parameter, {@code null} if not 179 * specified. 180 * 181 * @return This builder. 182 */ 183 public Builder contentType(final String cty) { 184 185 this.cty = cty; 186 return this; 187 } 188 189 190 /** 191 * Sets the critical header parameters ({@code crit}) 192 * parameter. 193 * 194 * @param crit The names of the critical header parameters, 195 * empty set or {@code null} if none. 196 * 197 * @return This builder. 198 */ 199 public Builder criticalParams(final Set<String> crit) { 200 201 this.crit = crit; 202 return this; 203 } 204 205 206 /** 207 * Sets a custom (non-registered) parameter. 208 * 209 * @param name The name of the custom parameter. Must not 210 * match a registered parameter name and must not 211 * be {@code null}. 212 * @param value The value of the custom parameter, should map 213 * to a valid JSON entity, {@code null} if not 214 * specified. 215 * 216 * @return This builder. 217 * 218 * @throws IllegalArgumentException If the specified parameter 219 * name matches a registered 220 * parameter name. 221 */ 222 public Builder customParam(final String name, final Object value) { 223 224 if (getRegisteredParameterNames().contains(name)) { 225 throw new IllegalArgumentException("The parameter name \"" + name + "\" matches a registered name"); 226 } 227 228 if (customParams == null) { 229 customParams = new HashMap<>(); 230 } 231 232 customParams.put(name, value); 233 234 return this; 235 } 236 237 238 /** 239 * Sets the custom (non-registered) parameters. The values must 240 * be serialisable to a JSON entity, otherwise will be ignored. 241 * 242 * @param customParameters The custom parameters, empty map or 243 * {@code null} if none. 244 * 245 * @return This builder. 246 */ 247 public Builder customParams(final Map<String, Object> customParameters) { 248 249 this.customParams = customParameters; 250 return this; 251 } 252 253 254 /** 255 * Sets the parsed Base64URL. 256 * 257 * @param base64URL The parsed Base64URL, {@code null} if the 258 * header is created from scratch. 259 * 260 * @return This builder. 261 */ 262 public Builder parsedBase64URL(final Base64URL base64URL) { 263 264 this.parsedBase64URL = base64URL; 265 return this; 266 } 267 268 269 /** 270 * Builds a new unsecured (plain) header. 271 * 272 * @return The unsecured header. 273 */ 274 public PlainHeader build() { 275 276 return new PlainHeader(typ, cty, crit, customParams, parsedBase64URL); 277 } 278 } 279 280 281 /** 282 * Creates a new minimal unsecured (plain) header with algorithm 283 * {@link Algorithm#NONE none}. 284 */ 285 public PlainHeader() { 286 287 this(null, null, null, null, null); 288 } 289 290 291 /** 292 * Creates a new unsecured (plain) header with algorithm 293 * {@link Algorithm#NONE none}. 294 * 295 * @param typ The type ({@code typ}) parameter, 296 * {@code null} if not specified. 297 * @param cty The content type ({@code cty}) parameter, 298 * {@code null} if not specified. 299 * @param crit The names of the critical header 300 * ({@code crit}) parameters, empty set or 301 * {@code null} if none. 302 * @param customParams The custom parameters, empty map or 303 * {@code null} if none. 304 * @param parsedBase64URL The parsed Base64URL, {@code null} if the 305 * header is created from scratch. 306 */ 307 public PlainHeader(final JOSEObjectType typ, 308 final String cty, 309 final Set<String> crit, 310 final Map<String, Object> customParams, 311 final Base64URL parsedBase64URL) { 312 313 super(Algorithm.NONE, typ, cty, crit, customParams, parsedBase64URL); 314 } 315 316 317 /** 318 * Deep copy constructor. 319 * 320 * @param plainHeader The unsecured header to copy. Must not be 321 * {@code null}. 322 */ 323 public PlainHeader(final PlainHeader plainHeader) { 324 325 this( 326 plainHeader.getType(), 327 plainHeader.getContentType(), 328 plainHeader.getCriticalParams(), 329 plainHeader.getCustomParams(), 330 plainHeader.getParsedBase64URL() 331 ); 332 } 333 334 335 /** 336 * Gets the registered parameter names for unsecured headers. 337 * 338 * @return The registered parameter names, as an unmodifiable set. 339 */ 340 public static Set<String> getRegisteredParameterNames() { 341 342 return REGISTERED_PARAMETER_NAMES; 343 } 344 345 346 /** 347 * Gets the algorithm ({@code alg}) parameter. 348 * 349 * @return {@link Algorithm#NONE}. 350 */ 351 @Override 352 public Algorithm getAlgorithm() { 353 354 return Algorithm.NONE; 355 } 356 357 358 /** 359 * Parses an unsecured header from the specified JSON object. 360 * 361 * @param jsonObject The JSON object to parse. Must not be {@code null}. 362 * 363 * @return The unsecured header. 364 * 365 * @throws ParseException If the specified JSON object doesn't 366 * represent a valid unsecured header. 367 */ 368 public static PlainHeader parse(final JSONObject jsonObject) 369 throws ParseException { 370 371 return parse(jsonObject, null); 372 } 373 374 375 /** 376 * Parses an unsecured header from the specified JSON object. 377 * 378 * @param jsonObject The JSON object to parse. Must not be 379 * {@code null}. 380 * @param parsedBase64URL The original parsed Base64URL, {@code null} 381 * if not applicable. 382 * 383 * @return The unsecured header. 384 * 385 * @throws ParseException If the specified JSON object doesn't 386 * represent a valid unsecured header. 387 */ 388 public static PlainHeader parse(final JSONObject jsonObject, 389 final Base64URL parsedBase64URL) 390 throws ParseException { 391 392 // Get the "alg" parameter 393 Algorithm alg = Header.parseAlgorithm(jsonObject); 394 395 if (alg != Algorithm.NONE) { 396 throw new ParseException("The algorithm \"alg\" header parameter must be \"none\"", 0); 397 } 398 399 PlainHeader.Builder header = new Builder().parsedBase64URL(parsedBase64URL); 400 401 // Parse optional + custom parameters 402 for(final String name: jsonObject.keySet()) { 403 404 405 406 if("alg".equals(name)) { 407 // skip 408 } else if("typ".equals(name)) { 409 header = header.type(new JOSEObjectType(JSONObjectUtils.getString(jsonObject, name))); 410 } else if("cty".equals(name)) { 411 header = header.contentType(JSONObjectUtils.getString(jsonObject, name)); 412 } else if("crit".equals(name)) { 413 header = header.criticalParams(new HashSet<>(JSONObjectUtils.getStringList(jsonObject, name))); 414 } else { 415 header = header.customParam(name, jsonObject.get(name)); 416 } 417 } 418 419 return header.build(); 420 } 421 422 423 /** 424 * Parses an unsecured header from the specified JSON string. 425 * 426 * @param jsonString The JSON string to parse. Must not be 427 * {@code null}. 428 * 429 * @return The unsecured header. 430 * 431 * @throws ParseException If the specified JSON string doesn't 432 * represent a valid unsecured header. 433 */ 434 public static PlainHeader parse(final String jsonString) 435 throws ParseException { 436 437 return parse(jsonString, null); 438 } 439 440 441 /** 442 * Parses an unsecured header from the specified JSON string. 443 * 444 * @param jsonString The JSON string to parse. Must not be 445 * {@code null}. 446 * @param parsedBase64URL The original parsed Base64URL, {@code null} 447 * if not applicable. 448 * 449 * @return The unsecured header. 450 * 451 * @throws ParseException If the specified JSON string doesn't 452 * represent a valid unsecured header. 453 */ 454 public static PlainHeader parse(final String jsonString, 455 final Base64URL parsedBase64URL) 456 throws ParseException { 457 458 return parse(JSONObjectUtils.parse(jsonString), parsedBase64URL); 459 } 460 461 462 /** 463 * Parses an unsecured header from the specified Base64URL. 464 * 465 * @param base64URL The Base64URL to parse. Must not be {@code null}. 466 * 467 * @return The unsecured header. 468 * 469 * @throws ParseException If the specified Base64URL doesn't represent 470 * a valid unsecured header. 471 */ 472 public static PlainHeader parse(final Base64URL base64URL) 473 throws ParseException { 474 475 return parse(base64URL.decodeToString(), base64URL); 476 } 477}