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