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.io.Serializable; 022import java.text.ParseException; 023 024import com.nimbusds.jose.util.Base64URL; 025import com.nimbusds.jose.util.JSONObjectUtils; 026import com.nimbusds.jose.util.StandardCharset; 027import com.nimbusds.jwt.SignedJWT; 028import net.jcip.annotations.Immutable; 029import net.minidev.json.JSONObject; 030 031 032/** 033 * Payload of an unsecured (plain), JSON Web Signature (JWS) or JSON Web 034 * Encryption (JWE) object. Supports JSON object, string, byte array, 035 * Base64URL, JWS object and signed JWT payload representations. This class is 036 * immutable. 037 * 038 * <p>UTF-8 is the character set for all conversions between strings and byte 039 * arrays. 040 * 041 * <p>Conversion relations: 042 * 043 * <pre> 044 * JSONObject <=> String <=> Base64URL 045 * <=> byte[] 046 * <=> JWSObject 047 * <=> SignedJWT 048 * </pre> 049 * 050 * @author Vladimir Dzhuvinov 051 * @version 2016-07-26 052 */ 053@Immutable 054public final class Payload implements Serializable { 055 056 057 /** 058 * Enumeration of the original data types used to create a 059 * {@link Payload}. 060 */ 061 public enum Origin { 062 063 064 /** 065 * The payload was created from a JSON object. 066 */ 067 JSON, 068 069 070 /** 071 * The payload was created from a string. 072 */ 073 STRING, 074 075 076 /** 077 * The payload was created from a byte array. 078 */ 079 BYTE_ARRAY, 080 081 082 /** 083 * The payload was created from a Base64URL-encoded object. 084 */ 085 BASE64URL, 086 087 088 /** 089 * The payload was created from a JWS object. 090 */ 091 JWS_OBJECT, 092 093 094 /** 095 * The payload was created from a signed JSON Web Token (JWT). 096 */ 097 SIGNED_JWT 098 } 099 100 101 private static final long serialVersionUID = 1L; 102 103 104 /** 105 * The original payload data type. 106 */ 107 private final Origin origin; 108 109 110 /** 111 * The JSON object representation. 112 */ 113 private final JSONObject jsonObject; 114 115 116 /** 117 * The string representation. 118 */ 119 private final String string; 120 121 122 /** 123 * The byte array representation. 124 */ 125 private final byte[] bytes; 126 127 128 /** 129 * The Base64URL representation. 130 */ 131 private final Base64URL base64URL; 132 133 134 /** 135 * The JWS object representation. 136 */ 137 private final JWSObject jwsObject; 138 139 140 /** 141 * The signed JWT representation. 142 */ 143 private final SignedJWT signedJWT; 144 145 146 /** 147 * Converts a byte array to a string using {@code UTF-8}. 148 * 149 * @param bytes The byte array to convert. May be {@code null}. 150 * 151 * @return The resulting string, {@code null} if conversion failed. 152 */ 153 private static String byteArrayToString(final byte[] bytes) { 154 155 return bytes != null ? new String(bytes, StandardCharset.UTF_8) : null; 156 } 157 158 159 /** 160 * Converts a string to a byte array using {@code UTF-8}. 161 * 162 * @param string The string to convert. May be {@code null}. 163 * 164 * @return The resulting byte array, {@code null} if conversion failed. 165 */ 166 private static byte[] stringToByteArray(final String string) { 167 168 return string != null ? string.getBytes(StandardCharset.UTF_8) : null; 169 } 170 171 172 /** 173 * Creates a new payload from the specified JSON object. 174 * 175 * @param jsonObject The JSON object representing the payload. Must not 176 * be {@code null}. 177 */ 178 public Payload(final JSONObject jsonObject) { 179 180 if (jsonObject == null) { 181 throw new IllegalArgumentException("The JSON object must not be null"); 182 } 183 184 this.jsonObject = jsonObject; 185 string = null; 186 bytes = null; 187 base64URL = null; 188 jwsObject = null; 189 signedJWT = null; 190 191 origin = Origin.JSON; 192 } 193 194 195 /** 196 * Creates a new payload from the specified string. 197 * 198 * @param string The string representing the payload. Must not be 199 * {@code null}. 200 */ 201 public Payload(final String string) { 202 203 if (string == null) { 204 throw new IllegalArgumentException("The string must not be null"); 205 } 206 207 jsonObject = null; 208 this.string = string; 209 bytes = null; 210 base64URL = null; 211 jwsObject = null; 212 signedJWT = null; 213 214 origin = Origin.STRING; 215 } 216 217 218 /** 219 * Creates a new payload from the specified byte array. 220 * 221 * @param bytes The byte array representing the payload. Must not be 222 * {@code null}. 223 */ 224 public Payload(final byte[] bytes) { 225 226 if (bytes == null) { 227 throw new IllegalArgumentException("The byte array must not be null"); 228 } 229 230 jsonObject = null; 231 string = null; 232 this.bytes = bytes; 233 base64URL = null; 234 jwsObject = null; 235 signedJWT = null; 236 237 origin = Origin.BYTE_ARRAY; 238 } 239 240 241 /** 242 * Creates a new payload from the specified Base64URL-encoded object. 243 * 244 * @param base64URL The Base64URL-encoded object representing the 245 * payload. Must not be {@code null}. 246 */ 247 public Payload(final Base64URL base64URL) { 248 249 if (base64URL == null) { 250 throw new IllegalArgumentException("The Base64URL-encoded object must not be null"); 251 } 252 253 jsonObject = null; 254 string = null; 255 bytes = null; 256 this.base64URL = base64URL; 257 jwsObject = null; 258 signedJWT = null; 259 260 origin = Origin.BASE64URL; 261 } 262 263 264 /** 265 * Creates a new payload from the specified JWS object. Intended for 266 * signed then encrypted JOSE objects. 267 * 268 * @param jwsObject The JWS object representing the payload. Must be in 269 * a signed state and not {@code null}. 270 */ 271 public Payload(final JWSObject jwsObject) { 272 273 if (jwsObject == null) { 274 throw new IllegalArgumentException("The JWS object must not be null"); 275 } 276 277 if (jwsObject.getState() == JWSObject.State.UNSIGNED) { 278 throw new IllegalArgumentException("The JWS object must be signed"); 279 } 280 281 jsonObject = null; 282 string = null; 283 bytes = null; 284 base64URL = null; 285 this.jwsObject = jwsObject; 286 signedJWT = null; 287 288 origin = Origin.JWS_OBJECT; 289 } 290 291 292 /** 293 * Creates a new payload from the specified signed JSON Web Token 294 * (JWT). Intended for signed then encrypted JWTs. 295 * 296 * @param signedJWT The signed JWT representing the payload. Must be in 297 * a signed state and not {@code null}. 298 */ 299 public Payload(final SignedJWT signedJWT) { 300 301 if (signedJWT == null) { 302 throw new IllegalArgumentException("The signed JWT must not be null"); 303 } 304 305 if (signedJWT.getState() == JWSObject.State.UNSIGNED) { 306 throw new IllegalArgumentException("The JWT must be signed"); 307 } 308 309 jsonObject = null; 310 string = null; 311 bytes = null; 312 base64URL = null; 313 this.signedJWT = signedJWT; 314 jwsObject = signedJWT; // The signed JWT is also a JWS 315 316 origin = Origin.SIGNED_JWT; 317 } 318 319 320 /** 321 * Gets the original data type used to create this payload. 322 * 323 * @return The payload origin. 324 */ 325 public Origin getOrigin() { 326 327 return origin; 328 } 329 330 331 /** 332 * Returns a JSON object representation of this payload. 333 * 334 * @return The JSON object representation, {@code null} if the payload 335 * couldn't be converted to a JSON object. 336 */ 337 public JSONObject toJSONObject() { 338 339 if (jsonObject != null) { 340 return jsonObject; 341 } 342 343 // Convert 344 345 String s = toString(); 346 347 if (s == null) { 348 // to string conversion failed 349 return null; 350 } 351 352 try { 353 return JSONObjectUtils.parse(s); 354 355 } catch (ParseException e) { 356 // Payload not a JSON object 357 return null; 358 } 359 } 360 361 362 /** 363 * Returns a string representation of this payload. 364 * 365 * @return The string representation. 366 */ 367 @Override 368 public String toString() { 369 370 if (string != null) { 371 372 return string; 373 } 374 375 // Convert 376 if (jwsObject != null) { 377 378 if (jwsObject.getParsedString() != null) { 379 return jwsObject.getParsedString(); 380 } else { 381 return jwsObject.serialize(); 382 } 383 384 } else if (jsonObject != null) { 385 386 return jsonObject.toString(); 387 388 } else if (bytes != null) { 389 390 return byteArrayToString(bytes); 391 392 } else if (base64URL != null) { 393 394 return base64URL.decodeToString(); 395 } else { 396 return null; // should never happen 397 } 398 } 399 400 401 /** 402 * Returns a byte array representation of this payload. 403 * 404 * @return The byte array representation. 405 */ 406 public byte[] toBytes() { 407 408 if (bytes != null) { 409 return bytes; 410 } 411 412 // Convert 413 if (base64URL != null) { 414 return base64URL.decode(); 415 416 } 417 418 return stringToByteArray(toString()); 419 } 420 421 422 /** 423 * Returns a Base64URL representation of this payload. 424 * 425 * @return The Base64URL representation. 426 */ 427 public Base64URL toBase64URL() { 428 429 if (base64URL != null) { 430 return base64URL; 431 } 432 433 // Convert 434 return Base64URL.encode(toBytes()); 435 } 436 437 438 /** 439 * Returns a JWS object representation of this payload. Intended for 440 * signed then encrypted JOSE objects. 441 * 442 * @return The JWS object representation, {@code null} if the payload 443 * couldn't be converted to a JWS object. 444 */ 445 public JWSObject toJWSObject() { 446 447 if (jwsObject != null) { 448 return jwsObject; 449 } 450 451 try { 452 return JWSObject.parse(toString()); 453 454 } catch (ParseException e) { 455 456 return null; 457 } 458 } 459 460 461 /** 462 * Returns a signed JSON Web Token (JWT) representation of this 463 * payload. Intended for signed then encrypted JWTs. 464 * 465 * @return The signed JWT representation, {@code null} if the payload 466 * couldn't be converted to a signed JWT. 467 */ 468 public SignedJWT toSignedJWT() { 469 470 if (signedJWT != null) { 471 return signedJWT; 472 } 473 474 try { 475 return SignedJWT.parse(toString()); 476 477 } catch (ParseException e) { 478 479 return null; 480 } 481 } 482 483 484 /** 485 * Returns a transformation of this payload. 486 * 487 * @param <T> Type of the result. 488 * @param transformer The payload transformer. Must not be 489 * {@code null}. 490 * 491 * @return The transformed payload. 492 */ 493 public <T> T toType(final PayloadTransformer<T> transformer) { 494 495 return transformer.transform(this); 496 } 497}