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