001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2026, 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.crypto; 019 020 021import com.nimbusds.jose.*; 022import com.nimbusds.jose.crypto.impl.AAD; 023import com.nimbusds.jose.crypto.impl.CriticalHeaderParamsDeferral; 024import com.nimbusds.jose.crypto.impl.ECDH; 025import com.nimbusds.jose.crypto.impl.ECDHCryptoProvider; 026import com.nimbusds.jose.crypto.opts.OptionUtils; 027import com.nimbusds.jose.crypto.utils.ECChecks; 028import com.nimbusds.jose.jwk.Curve; 029import com.nimbusds.jose.jwk.ECKey; 030import com.nimbusds.jose.util.Base64URL; 031 032import javax.crypto.SecretKey; 033import java.security.PrivateKey; 034import java.security.interfaces.ECPrivateKey; 035import java.security.interfaces.ECPublicKey; 036import java.util.Collections; 037import java.util.LinkedHashSet; 038import java.util.Set; 039 040 041/** 042 * Elliptic Curve Diffie-Hellman decrypter of 043 * {@link com.nimbusds.jose.JWEObject JWE objects} for curves using EC JWK 044 * keys. Expects a private EC key (with a P-256, P-384 or P-521 curve). 045 * 046 * <p>See RFC 7518 047 * <a href="https://tools.ietf.org/html/rfc7518#section-4.6">section 4.6</a> 048 * for more information. 049 * 050 * <p>For Curve25519/X25519, see {@link X25519Decrypter} instead. 051 * 052 * <p>This class is thread-safe. 053 * 054 * <p>Supports the following key management algorithms: 055 * 056 * <ul> 057 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES} 058 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW} 059 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW} 060 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW} 061 * </ul> 062 * 063 * <p>Supports the following elliptic curves: 064 * 065 * <ul> 066 * <li>{@link com.nimbusds.jose.jwk.Curve#P_256} 067 * <li>{@link com.nimbusds.jose.jwk.Curve#P_384} 068 * <li>{@link com.nimbusds.jose.jwk.Curve#P_521} 069 * </ul> 070 * 071 * <p>Supports the following content encryption algorithms: 072 * 073 * <ul> 074 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 075 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 076 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 077 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 078 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 079 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 080 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 081 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 082 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 083 * </ul> 084 * 085 * @author Vladimir Dzhuvinov 086 * @author Egor Puzanov 087 * @version 2026-09-16 088 */ 089public class ECDHDecrypter extends ECDHCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware { 090 091 092 /** 093 * The supported EC JWK curves by the ECDH crypto provider class. 094 */ 095 public static final Set<Curve> SUPPORTED_ELLIPTIC_CURVES; 096 097 098 static { 099 Set<Curve> curves = new LinkedHashSet<>(); 100 curves.add(Curve.P_256); 101 curves.add(Curve.P_384); 102 curves.add(Curve.P_521); 103 SUPPORTED_ELLIPTIC_CURVES = Collections.unmodifiableSet(curves); 104 } 105 106 107 /** 108 * The private EC key. 109 */ 110 private final PrivateKey privateKey; 111 112 113 /** 114 * The critical header policy. 115 */ 116 private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral(); 117 118 119 /** 120 * The configured options, empty set if none. 121 */ 122 private final Set<JWEDecrypterOption> opts; 123 124 125 /** 126 * Creates a new Elliptic Curve Diffie-Hellman decrypter. 127 * 128 * @param privateKey The private EC key. Must not be {@code null}. 129 * 130 * @throws JOSEException If the elliptic curve is not supported. 131 */ 132 public ECDHDecrypter(final ECPrivateKey privateKey) 133 throws JOSEException { 134 135 this(privateKey, (Set<String>) null, Collections.<JWEDecrypterOption>emptySet()); 136 } 137 138 139 /** 140 * Creates a new Elliptic Curve Diffie-Hellman decrypter. 141 * 142 * @param ecJWK The EC JSON Web Key (JWK). Must contain a private 143 * part. Must not be {@code null}. 144 * 145 * @throws JOSEException If the elliptic curve is not supported. 146 */ 147 public ECDHDecrypter(final ECKey ecJWK) 148 throws JOSEException { 149 150 this(ecJWK, null, Collections.<JWEDecrypterOption>emptySet()); 151 } 152 153 154 /** 155 * Creates a new Elliptic Curve Diffie-Hellman decrypter. 156 * 157 * @param ecJWK The EC JSON Web Key (JWK). Must contain a private 158 * part. Must not be {@code null}. 159 * @param opts The decryption options, empty or {@code null} if none. 160 * 161 * @throws JOSEException If the elliptic curve is not supported. 162 */ 163 public ECDHDecrypter(final ECKey ecJWK, 164 final Set<JWEDecrypterOption> opts) 165 throws JOSEException { 166 167 this(ecJWK, null, opts); 168 } 169 170 171 /** 172 * Creates a new Elliptic Curve Diffie-Hellman decrypter. 173 * 174 * @param ecJWK The EC JSON Web Key (JWK). Must contain a 175 * private part. Must not be {@code null}. 176 * @param defCritHeaders The names of the critical header parameters 177 * that are deferred to the application for 178 * processing, empty set or {@code null} if none. 179 * @param opts The decryption options, empty or {@code null} 180 * if none. 181 * 182 * @throws JOSEException If the elliptic curve is not supported. 183 */ 184 public ECDHDecrypter(final ECKey ecJWK, 185 final Set<String> defCritHeaders, 186 final Set<JWEDecrypterOption> opts) 187 throws JOSEException { 188 189 super(ecJWK.getCurve(), null); 190 191 if (! ecJWK.isPrivate()) { 192 throw new JOSEException("The EC JWK doesn't contain a private part"); 193 } 194 195 this.privateKey = ecJWK.toECPrivateKey(); 196 this.opts = opts != null ? opts : Collections.<JWEDecrypterOption>emptySet(); 197 critPolicy.setDeferredCriticalHeaderParams(defCritHeaders); 198 } 199 200 201 /** 202 * Creates a new Elliptic Curve Diffie-Hellman decrypter. 203 * 204 * @param privateKey The private EC key. Must not be {@code null}. 205 * @param defCritHeaders The names of the critical header parameters 206 * that are deferred to the application for 207 * processing, empty set or {@code null} if none. 208 * 209 * @throws JOSEException If the elliptic curve is not supported. 210 */ 211 public ECDHDecrypter(final ECPrivateKey privateKey, final Set<String> defCritHeaders) 212 throws JOSEException { 213 214 this(privateKey, defCritHeaders, Collections.<JWEDecrypterOption>emptySet()); 215 } 216 217 218 /** 219 * Creates a new Elliptic Curve Diffie-Hellman decrypter. 220 * 221 * @param privateKey The private EC key. Must not be {@code null}. 222 * @param defCritHeaders The names of the critical header parameters 223 * that are deferred to the application for 224 * processing, empty set or {@code null} if none. 225 * @param opts The decryption options, empty or {@code null} 226 * if none. 227 * 228 * @throws JOSEException If the elliptic curve is not supported. 229 */ 230 public ECDHDecrypter(final ECPrivateKey privateKey, 231 final Set<String> defCritHeaders, 232 final Set<JWEDecrypterOption> opts) 233 throws JOSEException { 234 235 this(privateKey, defCritHeaders, Curve.forECParameterSpec(privateKey.getParams()), opts); 236 } 237 238 239 /** 240 * Creates a new Elliptic Curve Diffie-Hellman decrypter. This 241 * constructor can also accept a private EC key located in a PKCS#11 242 * store that doesn't expose the private key parameters (such as a 243 * smart card or HSM). 244 * 245 * @param privateKey The private EC key. Must not be {@code null}. 246 * @param defCritHeaders The names of the critical header parameters 247 * that are deferred to the application for 248 * processing, empty set or {@code null} if none. 249 * @param curve The key curve. Must not be {@code null}. 250 * 251 * @throws JOSEException If the elliptic curve is not supported. 252 */ 253 public ECDHDecrypter(final PrivateKey privateKey, 254 final Set<String> defCritHeaders, 255 final Curve curve) 256 throws JOSEException { 257 258 this(privateKey, defCritHeaders, curve, Collections.<JWEDecrypterOption>emptySet()); 259 } 260 261 262 /** 263 * Creates a new Elliptic Curve Diffie-Hellman decrypter. This 264 * constructor can also accept a private EC key located in a PKCS#11 265 * store that doesn't expose the private key parameters (such as a 266 * smart card or HSM). 267 * 268 * @param privateKey The private EC key. Must not be {@code null}. 269 * @param defCritHeaders The names of the critical header parameters 270 * that are deferred to the application for 271 * processing, empty set or {@code null} if none. 272 * @param curve The key curve. Must not be {@code null}. 273 * @param opts The decryption options, empty or {@code null} 274 * if none. 275 * 276 * @throws JOSEException If the elliptic curve is not supported. 277 */ 278 public ECDHDecrypter(final PrivateKey privateKey, 279 final Set<String> defCritHeaders, 280 final Curve curve, 281 final Set<JWEDecrypterOption> opts) 282 throws JOSEException { 283 284 super(curve, null); 285 286 critPolicy.setDeferredCriticalHeaderParams(defCritHeaders); 287 288 this.privateKey = privateKey; 289 290 this.opts = opts != null ? opts : Collections.<JWEDecrypterOption>emptySet(); 291 } 292 293 294 /** 295 * Returns the private EC key. 296 * 297 * @return The private EC key. Casting to 298 * {@link java.security.interfaces.ECPrivateKey} may not be 299 * possible if the key is located in a PKCS#11 store that 300 * doesn't expose the private key parameters. 301 */ 302 public PrivateKey getPrivateKey() { 303 304 return privateKey; 305 } 306 307 308 @Override 309 public Set<Curve> supportedEllipticCurves() { 310 311 return SUPPORTED_ELLIPTIC_CURVES; 312 } 313 314 315 @Override 316 public Set<String> getProcessedCriticalHeaderParams() { 317 318 return critPolicy.getProcessedCriticalHeaderParams(); 319 } 320 321 322 @Override 323 public Set<String> getDeferredCriticalHeaderParams() { 324 325 return critPolicy.getDeferredCriticalHeaderParams(); 326 } 327 328 329 /** 330 * Decrypts the specified cipher text of a {@link JWEObject JWE 331 * Object}. 332 * 333 * @param header The JSON Web Encryption (JWE) header. Must 334 * specify a supported JWE algorithm and method. 335 * Must not be {@code null}. 336 * @param encryptedKey The encrypted key, {@code null} if not required 337 * by the JWE algorithm. 338 * @param iv The initialisation vector, {@code null} if not 339 * required by the JWE algorithm. 340 * @param cipherText The cipher text to decrypt. Must not be 341 * {@code null}. 342 * @param authTag The authentication tag, {@code null} if not 343 * required. 344 * 345 * @return The clear text. 346 * 347 * @throws JOSEException If the JWE algorithm or method is not 348 * supported, if a critical header parameter is 349 * not supported or marked for deferral to the 350 * application, or if decryption failed for some 351 * other reason. 352 */ 353 @Deprecated 354 public byte[] decrypt(final JWEHeader header, 355 final Base64URL encryptedKey, 356 final Base64URL iv, 357 final Base64URL cipherText, 358 final Base64URL authTag) 359 throws JOSEException { 360 361 return decrypt(header, encryptedKey, iv, cipherText, authTag, AAD.compute(header)); 362 } 363 364 365 @Override 366 public byte[] decrypt(final JWEHeader header, 367 final Base64URL encryptedKey, 368 final Base64URL iv, 369 final Base64URL cipherText, 370 final Base64URL authTag, 371 final byte[] aad) 372 throws JOSEException { 373 374 critPolicy.ensureHeaderPasses(header); 375 376 // Get ephemeral EC key 377 ECKey ephemeralKey = (ECKey) header.getEphemeralPublicKey(); 378 379 if (ephemeralKey == null) { 380 throw new JOSEException("Missing ephemeral public EC key \"epk\" JWE header parameter"); 381 } 382 383 ECPublicKey ephemeralPublicKey = ephemeralKey.toECPublicKey(); 384 385 // Curve check 386 if (getPrivateKey() instanceof ECPrivateKey) { 387 ECPrivateKey ecPrivateKey = (ECPrivateKey)getPrivateKey(); 388 if (!ECChecks.isPointOnCurve(ephemeralPublicKey, ecPrivateKey)) { 389 throw new JOSEException("Invalid ephemeral public EC key: Point(s) not on the expected curve"); 390 } 391 } else { 392 if (!ECChecks.isPointOnCurve(ephemeralPublicKey, getCurve().toECParameterSpec())) { 393 throw new JOSEException("Invalid ephemeral public EC key: Point(s) not on the expected curve"); 394 } 395 } 396 397 // Derive 'Z' 398 SecretKey Z = ECDH.deriveSharedSecret( 399 ephemeralPublicKey, 400 privateKey, 401 getJCAContext().getKeyEncryptionProvider()); 402 403 int maxDecompressedLength = OptionUtils.resolveMaxDecompressedPlainTextLength(opts); 404 405 return decryptWithZ(header, aad, Z, encryptedKey, iv, cipherText, authTag, maxDecompressedLength); 406 } 407}