001 package com.nimbusds.jose; 002 003 004 import java.text.ParseException; 005 006 import net.jcip.annotations.ThreadSafe; 007 008 import com.nimbusds.jose.util.Base64URL; 009 010 011 /** 012 * JSON Web Encryption (JWE) object. This class is thread-safe. 013 * 014 * @author Vladimir Dzhuvinov 015 * @version $version$ (2012-10-23) 016 */ 017 @ThreadSafe 018 public class JWEObject extends JOSEObject { 019 020 021 /** 022 * Enumeration of the states of a JSON Web Encryption (JWE) object. 023 */ 024 public static enum State { 025 026 027 /** 028 * The JWE object is created but not encrypted yet. 029 */ 030 UNENCRYPTED, 031 032 033 /** 034 * The JWE object is encrypted. 035 */ 036 ENCRYPTED, 037 038 039 /** 040 * The JWE object is decrypted. 041 */ 042 DECRYPTED; 043 } 044 045 046 /** 047 * The header. 048 */ 049 private final JWEHeader header; 050 051 052 /** 053 * The encrypted key, {@code null} if not computed or applicable. 054 */ 055 private Base64URL encryptedKey; 056 057 058 /** 059 * The initialisation vector, {@code null} if not generated or 060 * applicable. 061 */ 062 private Base64URL initializationVector; 063 064 065 /** 066 * The cipher text, {@code null} if not computed. 067 */ 068 private Base64URL cipherText; 069 070 071 /** 072 * The integrity value, {@code null} if not computed or applicable. 073 */ 074 private Base64URL integrityValue; 075 076 077 /** 078 * The JWE object state. 079 */ 080 private State state; 081 082 083 /** 084 * Creates a new to-be-encrypted JSON Web Encryption (JWE) object with 085 * the specified header and payload. The initial state will be 086 * {@link State#UNENCRYPTED unencrypted}. 087 * 088 * @param header The JWE header. Must not be {@code null}. 089 * @param payload The payload. Must not be {@code null}. 090 */ 091 public JWEObject(final JWEHeader header, Payload payload) { 092 093 if (header == null) 094 throw new IllegalArgumentException("The JWE header must not be null"); 095 096 this.header = header; 097 098 if (payload == null) 099 throw new IllegalArgumentException("The payload must not be null"); 100 101 setPayload(payload); 102 103 encryptedKey = null; 104 105 cipherText = null; 106 107 state = State.UNENCRYPTED; 108 } 109 110 111 /** 112 * Creates a new encrypted JSON Web Encryption (JWE) object with the 113 * specified serialised parts. The state will be {@link State#ENCRYPTED 114 * encrypted}. 115 * 116 * @param firstPart The first part, corresponding to the JWE header. 117 * Must not be {@code null}. 118 * @param secondPart The second part, corresponding to the encrypted 119 * key. Empty or {@code null} if none. 120 * @param thirdPart The third part, corresponding to the initialisation 121 * vector. Empty or {@code null} if none. 122 * @param fourthPart The fourth part, corresponding to the cipher text. 123 * Must not be {@code null}. 124 * @param fifthPart The fifth part, corresponding to the integrity 125 * value. Empty of {@code null} if none. 126 * 127 * @throws ParseException If parsing of the serialised parts failed. 128 */ 129 public JWEObject(final Base64URL firstPart, 130 final Base64URL secondPart, 131 final Base64URL thirdPart, 132 final Base64URL fourthPart, 133 final Base64URL fifthPart) 134 throws ParseException { 135 136 if (firstPart == null) 137 throw new IllegalArgumentException("The first part must not be null"); 138 139 try { 140 this.header = JWEHeader.parse(firstPart); 141 142 } catch (ParseException e) { 143 144 throw new ParseException("Invalid JWE header: " + e.getMessage(), 0); 145 } 146 147 if (secondPart == null || secondPart.toString().isEmpty()) 148 encryptedKey = null; 149 else 150 encryptedKey = secondPart; 151 152 if (thirdPart == null || thirdPart.toString().isEmpty()) 153 initializationVector = null; 154 else 155 initializationVector = thirdPart; 156 157 if (fourthPart == null) 158 throw new IllegalArgumentException("The fourth part must not be null"); 159 160 cipherText = fourthPart; 161 162 if (fifthPart == null || fifthPart.toString().isEmpty()) 163 integrityValue = null; 164 else 165 integrityValue = fifthPart; 166 167 state = State.ENCRYPTED; // but not decrypted yet! 168 169 setParsedParts(firstPart, secondPart, thirdPart, fourthPart, fifthPart); 170 } 171 172 173 @Override 174 public ReadOnlyJWEHeader getHeader() { 175 176 return header; 177 } 178 179 180 /** 181 * Gets the encrypted key of this JWE object. 182 * 183 * @return The encrypted key, {@code null} not applicable or the JWE 184 * object has not been encrypted yet. 185 */ 186 public Base64URL getEncryptedKey() { 187 188 return encryptedKey; 189 } 190 191 192 /** 193 * Gets the initialisation vector (IV) of this JWE object. 194 * 195 * @return The initialisation vector (IV), {@code null} if not 196 * applicable or the JWE object has not been encrypted yet. 197 */ 198 public Base64URL getInitializationVector() { 199 200 return initializationVector; 201 } 202 203 204 /** 205 * Gets the cipher text of this JWE object. 206 * 207 * @return The cipher text, {@code null} if the JWE object has not been 208 * encrypted yet. 209 */ 210 public Base64URL getCipherText() { 211 212 return cipherText; 213 } 214 215 216 /** 217 * Gets the integrity value of this JWE object. 218 * 219 * @return The integrity value, {@code null} if not applicable or the 220 * JWE object has not been encrypted yet. 221 */ 222 public Base64URL getIntegrityValue() { 223 224 return integrityValue; 225 } 226 227 228 /** 229 * Gets the state of this JWE object. 230 * 231 * @return The state. 232 */ 233 public State getState() { 234 235 return state; 236 } 237 238 239 /** 240 * Ensures the current state is {@link State#UNENCRYPTED unencrypted}. 241 * 242 * @throws IllegalStateException If the current state is not 243 * unencrypted. 244 */ 245 private void ensureUnencryptedState() { 246 247 if (state != State.UNENCRYPTED) 248 throw new IllegalStateException("The JWE object must be in an unencrypted state"); 249 } 250 251 252 /** 253 * Ensures the current state is {@link State#ENCRYPTED encrypted}. 254 * 255 * @throws IllegalStateException If the current state is not encrypted. 256 */ 257 private void ensureEncryptedState() { 258 259 if (state != State.ENCRYPTED) 260 throw new IllegalStateException("The JWE object must be in an encrypted state"); 261 } 262 263 264 /** 265 * Ensures the current state is {@link State#ENCRYPTED encrypted} or 266 * {@link State#DECRYPTED decrypted}. 267 * 268 * @throws IllegalStateException If the current state is not encrypted 269 * or decrypted. 270 */ 271 private void ensureEncryptedOrDecryptedState() { 272 273 if (state != State.ENCRYPTED && state != State.DECRYPTED) 274 throw new IllegalStateException("The JWE object must be in an encrypted or decrypted state"); 275 } 276 277 278 /** 279 * Ensures the specified JWE encrypter supports the algorithms of this 280 * JWE object. 281 * 282 * @throws JOSEException If the JWE algorithms are not supported. 283 */ 284 private void ensureJWEEncrypterSupport(final JWEEncrypter encrypter) 285 throws JOSEException { 286 287 if (! encrypter.supportedAlgorithms().contains(getHeader().getAlgorithm())) { 288 289 throw new JOSEException("The \"" + getHeader().getAlgorithm() + 290 "\" algorithm is not supported by the JWE encrypter"); 291 } 292 293 if (! encrypter.supportedEncryptionMethods().contains(getHeader().getEncryptionMethod())) { 294 295 throw new JOSEException("The \"" + getHeader().getEncryptionMethod() + 296 "\" encryption method is not supported by the JWE encrypter"); 297 } 298 } 299 300 301 /** 302 * Ensures the specified JWE decrypter accepts the algorithms and the 303 * headers of this JWE object. 304 * 305 * @throws JOSEException If the JWE algorithms or headers are not 306 * accepted. 307 */ 308 private void ensureJWEDecrypterAcceptance(final JWEDecrypter decrypter) 309 throws JOSEException { 310 311 JWEHeaderFilter filter = decrypter.getJWEHeaderFilter(); 312 313 if (filter == null) 314 return; 315 316 317 if (! filter.getAcceptedAlgorithms().contains(getHeader().getAlgorithm())) { 318 319 throw new JOSEException("The \"" + getHeader().getAlgorithm() + 320 "\" algorithm is not accepted by the JWE decrypter"); 321 } 322 323 324 if (! filter.getAcceptedEncryptionMethods().contains(getHeader().getEncryptionMethod())) { 325 326 throw new JOSEException("The \"" + getHeader().getEncryptionMethod() + 327 "\" encryption method is not accepted by the JWE decrypter"); 328 } 329 330 // Header params 331 332 if (! filter.getAcceptedParameters().containsAll(getHeader().getIncludedParameters())) { 333 334 throw new JOSEException("One or more header parameters not accepted by the JWE decrypter"); 335 } 336 } 337 338 339 /** 340 * Encrypts this JWE object with the specified encrypter. The JWE object 341 * must be in an {@link State#UNENCRYPTED unencrypted} state. 342 * 343 * @param encrypter The JWE encrypter. Must not be {@code null}. 344 * 345 * @throws IllegalStateException If the JWE object is not in an 346 * {@link State#UNENCRYPTED unencrypted 347 * state}. 348 * @throws JOSEException If the JWE object couldn't be 349 * encrypted. 350 */ 351 public synchronized void encrypt(final JWEEncrypter encrypter) 352 throws JOSEException { 353 354 ensureUnencryptedState(); 355 356 ensureJWEEncrypterSupport(encrypter); 357 358 JWECryptoParts parts = encrypter.encrypt(getHeader(), getPayload().toBytes()); 359 360 encryptedKey = parts.getEncryptedKey(); 361 initializationVector = parts.getInitializationVector(); 362 cipherText = parts.getCipherText(); 363 integrityValue = parts.getIntegrityValue(); 364 365 state = State.ENCRYPTED; 366 } 367 368 369 /** 370 * Decrypts this JWE object with the specified decrypter. The JWE object 371 * must be in a {@link State#ENCRYPTED encrypted} state. 372 * 373 * @param decrypter The JWE decrypter. Must not be {@code null}. 374 * 375 * @throws IllegalStateException If the JWE object is not in an 376 * {@link State#ENCRYPTED encrypted 377 * state}. 378 * @throws JOSEException If the JWE object couldn't be 379 * decrypted. 380 */ 381 public synchronized void decrypt(final JWEDecrypter decrypter) 382 throws JOSEException { 383 384 ensureEncryptedState(); 385 386 ensureJWEDecrypterAcceptance(decrypter); 387 388 setPayload(new Payload(decrypter.decrypt(getHeader(), 389 getEncryptedKey(), 390 getInitializationVector(), 391 getCipherText(), 392 getIntegrityValue()))); 393 394 state = State.DECRYPTED; 395 } 396 397 398 /** 399 * Serialises this JWE object to its compact format consisting of 400 * Base64URL-encoded parts delimited by period ('.') characters. It must 401 * be in a {@link State#ENCRYPTED encrypted} or 402 * {@link State#DECRYPTED decrypted} state. 403 * 404 * <pre> 405 * [header-base64url].[encryptedKey-base64url].[iv-base64url].[cipherText-base64url].[integrityValue-base64url] 406 * </pre> 407 * 408 * @return The serialised JWE object. 409 * 410 * @throws IllegalStateException If the JWE object is not in a 411 * {@link State#ENCRYPTED encrypted} or 412 * {@link State#DECRYPTED decrypted 413 * state}. 414 */ 415 @Override 416 public String serialize() { 417 418 ensureEncryptedOrDecryptedState(); 419 420 StringBuilder sb = new StringBuilder(header.toBase64URL().toString()); 421 sb.append('.'); 422 423 if (encryptedKey != null) 424 sb.append(encryptedKey.toString()); 425 426 sb.append('.'); 427 428 if (initializationVector != null) 429 sb.append(initializationVector.toString()); 430 431 sb.append('.'); 432 433 sb.append(cipherText.toString()); 434 435 sb.append('.'); 436 437 if (integrityValue != null) 438 sb.append(integrityValue.toString()); 439 440 return sb.toString(); 441 } 442 443 444 /** 445 * Parses a JWE object from the specified string in compact form. The 446 * parsed JWE object will be given an {@link State#ENCRYPTED} state. 447 * 448 * @param s The string to parse. Must not be {@code null}. 449 * 450 * @return The JWE object. 451 * 452 * @throws ParseException If the string couldn't be parsed to a valid JWE 453 * object. 454 */ 455 public static JWEObject parse(String s) 456 throws ParseException { 457 458 Base64URL[] parts = JOSEObject.split(s); 459 460 if (parts.length != 5) 461 throw new ParseException("Unexpected number of Base64URL parts, must be five", 0); 462 463 return new JWEObject(parts[0], parts[1], parts[2], parts[3], parts[4]); 464 } 465 }