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.jose.util.Base64URL; 012import com.nimbusds.jose.util.JSONObjectUtils; 013 014 015/** 016 * Payload with JSON object, string, byte array and Base64URL views. Represents 017 * the original object that was signed with JWS or encrypted with JWE. This 018 * class is immutable. 019 * 020 * <p>Non-initial views are created on demand to conserve resources. 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 * </pre> 031 * 032 * @author Vladimir Dzhuvinov 033 * @version $version$ (2013-05-16) 034 */ 035@Immutable 036public class Payload { 037 038 039 /** 040 * Enumeration of the original data types used to create a 041 * {@link Payload}. 042 */ 043 public static enum Origin { 044 045 046 /** 047 * The payload was created from a JSON object. 048 */ 049 JSON, 050 051 052 /** 053 * The payload was created from a string. 054 */ 055 STRING, 056 057 058 /** 059 * The payload was created from a byte array. 060 */ 061 BYTE_ARRAY, 062 063 064 /** 065 * The payload was created from a Base64URL-encoded object. 066 */ 067 BASE64URL; 068 } 069 070 071 /** 072 * UTF-8 is the character set for all conversions between strings and 073 * byte arrays. 074 */ 075 private static final Charset CHARSET = Charset.forName("UTF-8"); 076 077 078 /** 079 * The original payload data type. 080 */ 081 private Origin origin; 082 083 084 /** 085 * The JSON object view. 086 */ 087 private JSONObject jsonView = null; 088 089 090 /** 091 * The string view. 092 */ 093 private String stringView = null; 094 095 096 /** 097 * The byte array view. 098 */ 099 private byte[] bytesView = null; 100 101 102 /** 103 * The Base64URL view. 104 */ 105 private Base64URL base64URLView = null; 106 107 108 /** 109 * Converts a byte array to a string using {@link #CHARSET}. 110 * 111 * @param bytes The byte array to convert. May be {@code null}. 112 * 113 * @return The resulting string, {@code null} if conversion failed. 114 */ 115 private static String byteArrayToString(final byte[] bytes) { 116 117 if (bytes == null) { 118 119 return null; 120 } 121 122 return new String(bytes, CHARSET); 123 } 124 125 126 /** 127 * Converts a string to a byte array using {@link #CHARSET}. 128 * 129 * @param string The string to convert. May be {@code null}. 130 * 131 * @return The resulting byte array, {@code null} if conversion failed. 132 */ 133 private static byte[] stringToByteArray(final String string) { 134 135 if (string == null) { 136 137 return null; 138 } 139 140 return string.getBytes(CHARSET); 141 } 142 143 144 /** 145 * Creates a new payload from the specified JSON object. 146 * 147 * @param json The JSON object representing the payload. Must not be 148 * {@code null}. 149 */ 150 public Payload(final JSONObject json) { 151 152 if (json == null) { 153 throw new IllegalArgumentException("The JSON object must not be null"); 154 } 155 156 jsonView = json; 157 158 origin = Origin.JSON; 159 } 160 161 162 /** 163 * Creates a new payload from the specified string. 164 * 165 * @param string The string representing the payload. Must not be 166 * {@code null}. 167 */ 168 public Payload(final String string) { 169 170 if (string == null) { 171 172 throw new IllegalArgumentException("The string must not be null"); 173 } 174 175 stringView = string; 176 177 origin = Origin.STRING; 178 } 179 180 181 /** 182 * Creates a new payload from the specified byte array. 183 * 184 * @param bytes The byte array representing the payload. Must not be 185 * {@code null}. 186 */ 187 public Payload(final byte[] bytes) { 188 189 if (bytes == null) { 190 191 throw new IllegalArgumentException("The byte array must not be null"); 192 } 193 194 bytesView = bytes; 195 196 origin = Origin.BYTE_ARRAY; 197 } 198 199 200 /** 201 * Creates a new payload from the specified Base64URL-encoded object. 202 * 203 * @param base64URL The Base64URL-encoded object representing the 204 * payload. Must not be {@code null}. 205 */ 206 public Payload(final Base64URL base64URL) { 207 208 if (base64URL == null) { 209 210 throw new IllegalArgumentException("The Base64URL-encoded object must not be null"); 211 } 212 213 base64URLView = base64URL; 214 215 origin = Origin.BASE64URL; 216 } 217 218 219 /** 220 * Gets the original data type used to create this payload. 221 * 222 * @return The payload origin. 223 */ 224 public Origin getOrigin() { 225 226 return origin; 227 } 228 229 230 /** 231 * Returns a JSON object view of this payload. 232 * 233 * @return The JSON object view, {@code null} if the payload couldn't 234 * be converted to a JSON object. 235 */ 236 public JSONObject toJSONObject() { 237 238 if (jsonView != null) { 239 240 return jsonView; 241 } 242 243 // Convert 244 if (stringView != null) { 245 246 try { 247 jsonView = JSONObjectUtils.parseJSONObject(stringView); 248 249 } catch (ParseException e) { 250 251 // jsonView remains null 252 } 253 254 } else if (bytesView != null) { 255 256 stringView = byteArrayToString(bytesView); 257 258 try { 259 jsonView = JSONObjectUtils.parseJSONObject(stringView); 260 261 } catch (ParseException e) { 262 263 // jsonView remains null 264 } 265 266 } else if (base64URLView != null) { 267 268 stringView = base64URLView.decodeToString(); 269 270 try { 271 jsonView = JSONObjectUtils.parseJSONObject(stringView); 272 273 } catch (ParseException e) { 274 275 // jsonView remains null 276 } 277 } 278 279 return jsonView; 280 } 281 282 283 /** 284 * Returns a string view of this payload. 285 * 286 * @return The string view. 287 */ 288 @Override 289 public String toString() { 290 291 if (stringView != null) { 292 293 return stringView; 294 } 295 296 // Convert 297 if (jsonView != null) { 298 299 stringView = jsonView.toString(); 300 301 } else if (bytesView != null) { 302 303 stringView = byteArrayToString(bytesView); 304 305 } else if (base64URLView != null) { 306 307 stringView = base64URLView.decodeToString(); 308 } 309 310 return stringView; 311 } 312 313 314 /** 315 * Returns a byte array view of this payload. 316 * 317 * @return The byte array view. 318 */ 319 public byte[] toBytes() { 320 321 if (bytesView != null) { 322 323 return bytesView; 324 } 325 326 // Convert 327 if (stringView != null) { 328 329 bytesView = stringToByteArray(stringView); 330 331 } else if (jsonView != null) { 332 333 stringView = jsonView.toString(); 334 bytesView = stringToByteArray(stringView); 335 336 } else if (base64URLView != null) { 337 338 bytesView = base64URLView.decode(); 339 } 340 341 return bytesView; 342 } 343 344 345 /** 346 * Returns a Base64URL view of this payload. 347 * 348 * @return The Base64URL view. 349 */ 350 public Base64URL toBase64URL() { 351 352 if (base64URLView != null) { 353 354 return base64URLView; 355 } 356 357 // Convert 358 359 if (stringView != null) { 360 361 base64URLView = Base64URL.encode(stringView); 362 363 } else if (bytesView != null) { 364 365 base64URLView = Base64URL.encode(bytesView); 366 367 } else if (jsonView != null) { 368 369 stringView = jsonView.toString(); 370 base64URLView = Base64URL.encode(stringView); 371 } 372 373 return base64URLView; 374 } 375}