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.jwk.Curve; 028import com.nimbusds.jose.jwk.OctetKeyPair; 029import com.nimbusds.jose.util.Base64URL; 030 031import javax.crypto.SecretKey; 032import java.util.Collections; 033import java.util.Set; 034 035 036/** 037 * Curve25519 Elliptic Curve Diffie-Hellman decrypter of 038 * {@link com.nimbusds.jose.JWEObject JWE objects}. 039 * Expects a private {@link OctetKeyPair} key with {@code "crv"} X25519. 040 * 041 * <p>See <a href="https://tools.ietf.org/html/rfc8037">RFC 8037</a> 042 * for more information. 043 * 044 * <p>See also {@link ECDHDecrypter} for ECDH on other curves. 045 * 046 * <p>This class is thread-safe. 047 * 048 * <p>Supports the following key management algorithms: 049 * 050 * <ul> 051 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES} 052 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW} 053 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW} 054 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW} 055 * </ul> 056 * 057 * <p>Supports the following elliptic curve: 058 * 059 * <ul> 060 * <li>{@link com.nimbusds.jose.jwk.Curve#X25519} (Curve25519) 061 * </ul> 062 * 063 * <p>Supports the following content encryption algorithms: 064 * 065 * <ul> 066 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 067 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 068 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 069 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 070 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 071 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 072 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 073 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 074 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 075 * </ul> 076 * 077 * @author Tim McLean 078 * @author Egor Puzanov 079 * @author Vladimir Dzhuvinov 080 * @version 2026-09-16 081 */ 082public class X25519Decrypter extends ECDHCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware { 083 084 085 /** 086 * The private key. 087 */ 088 private final OctetKeyPair privateKey; 089 090 091 /** 092 * The critical header policy. 093 */ 094 private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral(); 095 096 097 /** 098 * The configured options, empty set if none. 099 */ 100 private final Set<JWEDecrypterOption> opts; 101 102 103 /** 104 * Creates a new Curve25519 Elliptic Curve Diffie-Hellman decrypter. 105 * 106 * @param privateKey The private key. Must not be {@code null}. 107 * 108 * @throws JOSEException If the key subtype is not supported. 109 */ 110 public X25519Decrypter(final OctetKeyPair privateKey) 111 throws JOSEException { 112 113 this(privateKey, null, Collections.<JWEDecrypterOption>emptySet()); 114 } 115 116 117 /** 118 * Creates a new Curve25519 Elliptic Curve Diffie-Hellman decrypter. 119 * 120 * @param privateKey The private key. Must not be {@code null}. 121 * @param defCritHeaders The names of the critical header parameters 122 * that are deferred to the application for 123 * processing, empty set or {@code null} if none. 124 * 125 * @throws JOSEException If the key subtype is not supported. 126 */ 127 public X25519Decrypter(final OctetKeyPair privateKey, final Set<String> defCritHeaders) 128 throws JOSEException { 129 130 this(privateKey, defCritHeaders, Collections.<JWEDecrypterOption>emptySet()); 131 } 132 133 134 /** 135 * Creates a new Curve25519 Elliptic Curve Diffie-Hellman decrypter. 136 * 137 * @param privateKey The private key. Must not be {@code null}. 138 * @param defCritHeaders The names of the critical header parameters 139 * that are deferred to the application for 140 * processing, empty set or {@code null} if none. 141 * @param opts The decryption options, empty or {@code null} 142 * if none. 143 * 144 * @throws JOSEException If the key subtype is not supported. 145 */ 146 public X25519Decrypter(final OctetKeyPair privateKey, 147 final Set<String> defCritHeaders, 148 final Set<JWEDecrypterOption> opts) 149 throws JOSEException { 150 151 super(privateKey.getCurve(), null); 152 153 if (! Curve.X25519.equals(privateKey.getCurve())) { 154 throw new JOSEException("X25519Decrypter only supports OctetKeyPairs with crv=X25519"); 155 } 156 157 if (! privateKey.isPrivate()) { 158 throw new JOSEException("The OctetKeyPair doesn't contain a private part"); 159 } 160 161 this.privateKey = privateKey; 162 163 this.opts = opts != null ? opts : Collections.<JWEDecrypterOption>emptySet(); 164 165 critPolicy.setDeferredCriticalHeaderParams(defCritHeaders); 166 } 167 168 169 @Override 170 public Set<Curve> supportedEllipticCurves() { 171 172 return Collections.singleton(Curve.X25519); 173 } 174 175 176 /** 177 * Returns the private key. 178 * 179 * @return The private key. 180 */ 181 public OctetKeyPair getPrivateKey() { 182 183 return privateKey; 184 } 185 186 187 @Override 188 public Set<String> getProcessedCriticalHeaderParams() { 189 190 return critPolicy.getProcessedCriticalHeaderParams(); 191 } 192 193 194 @Override 195 public Set<String> getDeferredCriticalHeaderParams() { 196 197 return critPolicy.getDeferredCriticalHeaderParams(); 198 } 199 200 201 /** 202 * Decrypts the specified cipher text of a {@link JWEObject JWE Object}. 203 * 204 * @param header The JSON Web Encryption (JWE) header. Must 205 * specify a supported JWE algorithm and method. 206 * Must not be {@code null}. 207 * @param encryptedKey The encrypted key, {@code null} if not required 208 * by the JWE algorithm. 209 * @param iv The initialisation vector, {@code null} if not 210 * required by the JWE algorithm. 211 * @param cipherText The cipher text to decrypt. Must not be 212 * {@code null}. 213 * @param authTag The authentication tag, {@code null} if not 214 * required. 215 * 216 * @return The clear text. 217 * 218 * @throws JOSEException If the JWE algorithm or method is not 219 * supported, if a critical header parameter is 220 * not supported or marked for deferral to the 221 * application, or if decryption failed for some 222 * other reason. 223 */ 224 @Deprecated 225 public byte[] decrypt(final JWEHeader header, 226 final Base64URL encryptedKey, 227 final Base64URL iv, 228 final Base64URL cipherText, 229 final Base64URL authTag) 230 throws JOSEException { 231 232 return decrypt(header, encryptedKey, iv, cipherText, authTag, AAD.compute(header)); 233 } 234 235 236 @Override 237 public byte[] decrypt(final JWEHeader header, 238 final Base64URL encryptedKey, 239 final Base64URL iv, 240 final Base64URL cipherText, 241 final Base64URL authTag, 242 final byte[] aad) 243 throws JOSEException { 244 245 // Check for unrecognizable "crit" properties 246 critPolicy.ensureHeaderPasses(header); 247 248 // Get ephemeral key from header 249 OctetKeyPair ephemeralPublicKey = (OctetKeyPair) header.getEphemeralPublicKey(); 250 251 if (ephemeralPublicKey == null) { 252 throw new JOSEException("Missing ephemeral public key epk JWE header parameter"); 253 } 254 255 if (! privateKey.getCurve().equals(ephemeralPublicKey.getCurve())) { 256 throw new JOSEException("Curve of ephemeral public key does not match curve of private key"); 257 } 258 259 // Derive 'Z' 260 // Note: X25519 does not require public key validation 261 // See https://cr.yp.to/ecdh.html#validate 262 SecretKey Z = ECDH.deriveSharedSecret(ephemeralPublicKey, privateKey); 263 264 int maxDecompressedLength = OptionUtils.resolveMaxDecompressedPlainTextLength(opts); 265 266 return decryptWithZ(header, aad, Z, encryptedKey, iv, cipherText, authTag, maxDecompressedLength); 267 } 268}