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