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 com.nimbusds.jose.crypto.impl.AAD; 022import com.nimbusds.jose.crypto.opts.MaxCompressedCipherTextLength; 023import com.nimbusds.jose.util.Base64URL; 024import net.jcip.annotations.ThreadSafe; 025 026import java.text.ParseException; 027import java.util.Objects; 028import java.util.Set; 029 030 031/** 032 * JSON Web Encryption (JWE) secured object with 033 * <a href="https://datatracker.ietf.org/doc/html/rfc7516#section-7.1">compact 034 * serialisation</a>. 035 * 036 * <p>This class is thread-safe. 037 * 038 * @author Vladimir Dzhuvinov 039 * @author Egor Puzanov 040 * @version 2026-01-04 041 */ 042@ThreadSafe 043public class JWEObject extends JOSEObject { 044 045 046 private static final long serialVersionUID = 1L; 047 048 049 /** 050 * The maximum allowed character length of compressed cipher text. 051 */ 052 public static final int MAX_COMPRESSED_CIPHER_TEXT_LENGTH = 100_000; 053 054 055 /** 056 * Enumeration of the states of a JSON Web Encryption (JWE) secured 057 * object. 058 */ 059 public enum State { 060 061 062 /** 063 * The JWE secured object is created but not encrypted yet. 064 */ 065 UNENCRYPTED, 066 067 068 /** 069 * The JWE secured object is encrypted. 070 */ 071 ENCRYPTED, 072 073 074 /** 075 * The JWE secured object is decrypted. 076 */ 077 DECRYPTED 078 } 079 080 081 /** 082 * The header. 083 */ 084 private JWEHeader header; 085 086 087 /** 088 * The encrypted key, {@code null} if not computed or applicable. 089 */ 090 private Base64URL encryptedKey; 091 092 093 /** 094 * The initialisation vector, {@code null} if not generated or 095 * applicable. 096 */ 097 private Base64URL iv; 098 099 100 /** 101 * The cipher text, {@code null} if not computed. 102 */ 103 private Base64URL cipherText; 104 105 106 /** 107 * The authentication tag, {@code null} if not computed or applicable. 108 */ 109 private Base64URL authTag; 110 111 112 /** 113 * The JWE object state. 114 */ 115 private State state; 116 117 118 /** 119 * Creates a new to-be-encrypted JSON Web Encryption (JWE) object with 120 * the specified header and payload. The initial state will be 121 * {@link State#UNENCRYPTED unencrypted}. 122 * 123 * @param header The JWE header. Must not be {@code null}. 124 * @param payload The payload. Must not be {@code null}. 125 */ 126 public JWEObject(final JWEHeader header, final Payload payload) { 127 128 this.header = Objects.requireNonNull(header); 129 setPayload(Objects.requireNonNull(payload)); 130 encryptedKey = null; 131 cipherText = null; 132 state = State.UNENCRYPTED; 133 } 134 135 136 /** 137 * Creates a new encrypted JSON Web Encryption (JWE) object with the 138 * specified serialised parts. The state will be {@link State#ENCRYPTED 139 * encrypted}. 140 * 141 * @param firstPart The first part, corresponding to the JWE header. 142 * Must not be {@code null}. 143 * @param secondPart The second part, corresponding to the encrypted 144 * key. Empty or {@code null} if none. 145 * @param thirdPart The third part, corresponding to the 146 * initialisation vector. Empty or {@code null} if 147 * none. 148 * @param fourthPart The fourth part, corresponding to the cipher text. 149 * Must not be {@code null}. 150 * @param fifthPart The fifth part, corresponding to the 151 * authentication tag. Empty of {@code null} if none. 152 * 153 * @throws ParseException If parsing of the serialised parts failed. 154 */ 155 public JWEObject(final Base64URL firstPart, 156 final Base64URL secondPart, 157 final Base64URL thirdPart, 158 final Base64URL fourthPart, 159 final Base64URL fifthPart) 160 throws ParseException { 161 162 try { 163 this.header = JWEHeader.parse(Objects.requireNonNull(firstPart)); 164 } catch (ParseException e) { 165 throw new ParseException("Invalid JWE header: " + e.getMessage(), 0); 166 } 167 168 if (secondPart == null || secondPart.toString().isEmpty()) { 169 170 encryptedKey = null; 171 172 } else { 173 174 encryptedKey = secondPart; 175 } 176 177 if (thirdPart == null || thirdPart.toString().isEmpty()) { 178 179 iv = null; 180 181 } else { 182 183 iv = thirdPart; 184 } 185 186 cipherText = Objects.requireNonNull(fourthPart); 187 188 if (fifthPart == null || fifthPart.toString().isEmpty()) { 189 190 authTag = null; 191 192 } else { 193 194 authTag = fifthPart; 195 } 196 197 state = State.ENCRYPTED; // but not decrypted yet! 198 199 setParsedParts(firstPart, secondPart, thirdPart, fourthPart, fifthPart); 200 } 201 202 203 @Override 204 public JWEHeader getHeader() { 205 206 return header; 207 } 208 209 210 /** 211 * Returns the encrypted key of this JWE object. 212 * 213 * @return The encrypted key, {@code null} not applicable or the JWE 214 * object has not been encrypted yet. 215 */ 216 public Base64URL getEncryptedKey() { 217 218 return encryptedKey; 219 } 220 221 222 /** 223 * Returns the initialisation vector (IV) of this JWE object. 224 * 225 * @return The initialisation vector (IV), {@code null} if not 226 * applicable or the JWE object has not been encrypted yet. 227 */ 228 public Base64URL getIV() { 229 230 return iv; 231 } 232 233 234 /** 235 * Returns the cipher text of this JWE object. 236 * 237 * @return The cipher text, {@code null} if the JWE object has not been 238 * encrypted yet. 239 */ 240 public Base64URL getCipherText() { 241 242 return cipherText; 243 } 244 245 246 /** 247 * Returns the authentication tag of this JWE object. 248 * 249 * @return The authentication tag, {@code null} if not applicable or 250 * the JWE object has not been encrypted yet. 251 */ 252 public Base64URL getAuthTag() { 253 254 return authTag; 255 } 256 257 258 /** 259 * Returns the state of the JWE secured object. 260 * 261 * @return The state. 262 */ 263 public State getState() { 264 265 return state; 266 } 267 268 269 /** 270 * Ensures the current state is {@link State#UNENCRYPTED unencrypted}. 271 * 272 * @throws IllegalStateException If the current state is not 273 * unencrypted. 274 */ 275 private void ensureUnencryptedState() { 276 277 if (state != State.UNENCRYPTED) { 278 279 throw new IllegalStateException("The JWE object must be in an unencrypted state"); 280 } 281 } 282 283 284 /** 285 * Ensures the current state is {@link State#ENCRYPTED encrypted}. 286 * 287 * @throws IllegalStateException If the current state is not encrypted. 288 */ 289 private void ensureEncryptedState() { 290 291 if (state != State.ENCRYPTED) { 292 293 throw new IllegalStateException("The JWE object must be in an encrypted state"); 294 } 295 } 296 297 298 /** 299 * Ensures the current state is {@link State#ENCRYPTED encrypted} or 300 * {@link State#DECRYPTED decrypted}. 301 * 302 * @throws IllegalStateException If the current state is not encrypted 303 * or decrypted. 304 */ 305 private void ensureEncryptedOrDecryptedState() { 306 307 if (state != State.ENCRYPTED && state != State.DECRYPTED) { 308 309 throw new IllegalStateException("The JWE object must be in an encrypted or decrypted state"); 310 } 311 } 312 313 314 /** 315 * Ensures the specified JWE encrypter supports the algorithms of this 316 * JWE object. 317 * 318 * @throws JOSEException If the JWE algorithms are not supported. 319 */ 320 private void ensureJWEEncrypterSupport(final JWEEncrypter encrypter) 321 throws JOSEException { 322 323 if (! encrypter.supportedJWEAlgorithms().contains(getHeader().getAlgorithm())) { 324 325 throw new JOSEException("The " + getHeader().getAlgorithm() + 326 " algorithm is not supported by the JWE encrypter: Supported algorithms: " + encrypter.supportedJWEAlgorithms()); 327 } 328 329 if (! encrypter.supportedEncryptionMethods().contains(getHeader().getEncryptionMethod())) { 330 331 throw new JOSEException("The " + getHeader().getEncryptionMethod() + 332 " encryption method or key size is not supported by the JWE encrypter: Supported methods: " + encrypter.supportedEncryptionMethods()); 333 } 334 } 335 336 337 /** 338 * Encrypts this JWE object with the specified encrypter. The JWE 339 * object must be in an {@link State#UNENCRYPTED unencrypted} state. 340 * 341 * @param encrypter The JWE encrypter. Must not be {@code null}. 342 * 343 * @throws IllegalStateException If the JWE object is not in an 344 * {@link State#UNENCRYPTED unencrypted 345 * state}. 346 * @throws JOSEException If the JWE object couldn't be 347 * encrypted. 348 */ 349 public synchronized void encrypt(final JWEEncrypter encrypter) 350 throws JOSEException { 351 352 ensureUnencryptedState(); 353 354 ensureJWEEncrypterSupport(encrypter); 355 356 JWECryptoParts parts; 357 358 try { 359 parts = encrypter.encrypt(getHeader(), getPayload().toBytes(), AAD.compute(getHeader())); 360 361 } catch (JOSEException e) { 362 363 throw e; 364 365 } catch (Exception e) { 366 367 // Prevent throwing unchecked exceptions at this point, 368 // see issue #20 369 throw new JOSEException(e.getMessage(), e); 370 } 371 372 // Check if the header has been modified 373 if (parts.getHeader() != null) { 374 header = parts.getHeader(); 375 } 376 377 encryptedKey = parts.getEncryptedKey(); 378 iv = parts.getInitializationVector(); 379 cipherText = parts.getCipherText(); 380 authTag = parts.getAuthenticationTag(); 381 382 state = State.ENCRYPTED; 383 } 384 385 386 /** 387 * Decrypts this JWE object with the specified decrypter. The JWE 388 * object must be in a {@link State#ENCRYPTED encrypted} state. 389 * 390 * @param decrypter The JWE decrypter. Must not be {@code null}. 391 * 392 * @throws IllegalStateException If the JWE object is not in an 393 * {@link State#ENCRYPTED encrypted 394 * state}. 395 * @throws JOSEException If the JWE object couldn't be 396 * decrypted. 397 */ 398 public synchronized void decrypt(final JWEDecrypter decrypter) 399 throws JOSEException { 400 401 decrypt(decrypter, null); 402 } 403 404 405 /** 406 * Decrypts this JWE object with the specified decrypter and options. 407 * The JWE object must be in a {@link State#ENCRYPTED encrypted} state. 408 * 409 * @param decrypter The JWE decrypter. Must not be {@code null}. 410 * @param opts The JWE decrypter options, {@code null} if none. 411 * 412 * @throws IllegalStateException If the JWE object is not in an 413 * {@link State#ENCRYPTED encrypted 414 * state}. 415 * @throws JOSEException If the JWE object couldn't be 416 * decrypted. 417 */ 418 public synchronized void decrypt(final JWEDecrypter decrypter, 419 final Set<JWEDecrypterOption> opts) 420 throws JOSEException { 421 422 ensureEncryptedState(); 423 424 if (getHeader().getCompressionAlgorithm() != null) { 425 426 int maxLength = resolveMaxCompressedCipherTextLength(opts); 427 428 if (getCipherText().toString().length() > maxLength) { 429 430 throw new JOSEException( 431 "The JWE compressed cipher text exceeds the " + 432 "maximum allowed length of " + 433 maxLength + 434 " characters"); 435 } 436 } 437 438 try { 439 setPayload(new Payload(decrypter.decrypt(getHeader(), 440 getEncryptedKey(), 441 getIV(), 442 getCipherText(), 443 getAuthTag(), 444 AAD.compute(getHeader())))); 445 446 } catch (JOSEException e) { 447 448 throw e; 449 450 } catch (Exception e) { 451 452 // Prevent throwing unchecked exceptions at this point, 453 // see issue #20 454 throw new JOSEException(e.getMessage(), e); 455 } 456 457 state = State.DECRYPTED; 458 } 459 460 461 /** 462 * Resolves the maximum compressed cipher text length from the 463 * specified options. 464 * 465 * @param opts The options, {@code null} if none. 466 * 467 * @return The maximum compressed cipher text length. 468 */ 469 private static int resolveMaxCompressedCipherTextLength(final Set<JWEDecrypterOption> opts) { 470 471 if (opts != null) { 472 for (JWEDecrypterOption opt : opts) { 473 if (opt instanceof MaxCompressedCipherTextLength) { 474 return ((MaxCompressedCipherTextLength) opt).getMaxLength(); 475 } 476 } 477 } 478 return MAX_COMPRESSED_CIPHER_TEXT_LENGTH; 479 } 480 481 482 /** 483 * Serialises this JWE object to its compact format consisting of 484 * Base64URL-encoded parts delimited by period ('.') characters. It 485 * must be in a {@link State#ENCRYPTED encrypted} or 486 * {@link State#DECRYPTED decrypted} state. 487 * 488 * <pre> 489 * [header-base64url].[encryptedKey-base64url].[iv-base64url].[cipherText-base64url].[authTag-base64url] 490 * </pre> 491 * 492 * @return The serialised JWE object. 493 * 494 * @throws IllegalStateException If the JWE object is not in a 495 * {@link State#ENCRYPTED encrypted} or 496 * {@link State#DECRYPTED decrypted 497 * state}. 498 */ 499 @Override 500 public String serialize() { 501 502 ensureEncryptedOrDecryptedState(); 503 504 StringBuilder sb = new StringBuilder(header.toBase64URL().toString()); 505 sb.append('.'); 506 507 if (encryptedKey != null) { 508 sb.append(encryptedKey); 509 } 510 511 sb.append('.'); 512 513 if (iv != null) { 514 sb.append(iv); 515 } 516 517 sb.append('.'); 518 sb.append(cipherText); 519 sb.append('.'); 520 521 if (authTag != null) { 522 sb.append(authTag); 523 } 524 525 return sb.toString(); 526 } 527 528 529 /** 530 * Parses a JWE object from the specified string in compact form. The 531 * parsed JWE object will be given an {@link State#ENCRYPTED} state. 532 * 533 * @param s The string to parse. Must not be {@code null}. 534 * 535 * @return The JWE object. 536 * 537 * @throws ParseException If the string couldn't be parsed to a valid 538 * JWE object. 539 */ 540 public static JWEObject parse(final String s) 541 throws ParseException { 542 543 Base64URL[] parts = JOSEObject.split(s); 544 545 if (parts.length != 5) { 546 547 throw new ParseException("Unexpected number of Base64URL parts, must be five", 0); 548 } 549 550 return new JWEObject(parts[0], parts[1], parts[2], parts[3], parts[4]); 551 } 552}