001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2021, Connect2id Ltd and contributors.
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.CriticalHeaderParamsAware;
022import com.nimbusds.jose.JOSEException;
023import com.nimbusds.jose.JWEDecrypter;
024import com.nimbusds.jose.JWEHeader;
025import com.nimbusds.jose.crypto.impl.*;
026import com.nimbusds.jose.jwk.Curve;
027import com.nimbusds.jose.jwk.ECKey;
028import com.nimbusds.jose.util.Base64URL;
029import net.jcip.annotations.ThreadSafe;
030
031import javax.crypto.SecretKey;
032import java.security.PrivateKey;
033import java.security.interfaces.ECPrivateKey;
034import java.security.interfaces.ECPublicKey;
035import java.util.Collections;
036import java.util.LinkedHashSet;
037import java.util.Set;
038
039
040/**
041 * Elliptic Curve Diffie-Hellman decrypter of
042 * {@link com.nimbusds.jose.JWEObject JWE objects} for curves using an EC JWK.
043 * Expects a private EC key (with a P-256, P-384 or P-521 curve).
044 *
045 * <p>Public Key Authenticated Encryption for JOSE
046 * <a href="https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04">ECDH-1PU</a>
047 * for more information.
048 *
049 * <p>For Curve25519/X25519, see {@link ECDH1PUX25519Decrypter} 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_1PU}
057 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU_A128KW}
058 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU_A192KW}
059 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU_A256KW}
060 * </ul>
061 *
062 * <p>Supports the following elliptic curves:
063 *
064 * <ul>
065 *     <li>{@link Curve#P_256}
066 *     <li>{@link Curve#P_384}
067 *     <li>{@link Curve#P_521}
068 * </ul>
069 *
070 * <p>Supports the following content encryption algorithms for Direct key
071 * agreement mode:
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 * <p>Supports the following content encryption algorithms for Key wrapping
086 * mode:
087 *
088 * <ul>
089 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
090 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
091 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
092 * </ul>
093 *
094 * @author Alexander Martynov
095 * @version 2021-08-03
096 */
097@ThreadSafe
098public class ECDH1PUDecrypter extends ECDH1PUCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware {
099
100
101    /**
102     * The supported EC JWK curves by the ECDH crypto provider class.
103     */
104    public static final Set<Curve> SUPPORTED_ELLIPTIC_CURVES;
105
106
107    static {
108        Set<Curve> curves = new LinkedHashSet<>();
109        curves.add(Curve.P_256);
110        curves.add(Curve.P_384);
111        curves.add(Curve.P_521);
112        SUPPORTED_ELLIPTIC_CURVES = Collections.unmodifiableSet(curves);
113    }
114
115
116    /**
117     * The private EC key.
118     */
119    private final ECPrivateKey privateKey;
120
121    /**
122     * The public EC key.
123     */
124    private final ECPublicKey publicKey;
125
126    /**
127     * The critical header policy.
128     */
129    private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
130
131
132    /**
133     * Creates a new Elliptic Curve Diffie-Hellman decrypter.
134     *
135     * @param privateKey        The private EC key. Must not be {@code null}.
136     * @param publicKey         The public EC key. Must not be {@code null}.
137     *
138     * @throws JOSEException If the elliptic curve is not supported.
139     */
140    public ECDH1PUDecrypter(final ECPrivateKey privateKey, final ECPublicKey publicKey)
141        throws JOSEException {
142
143        this(privateKey, publicKey, null);
144    }
145
146    /**
147     * Creates a new Elliptic Curve Diffie-Hellman decrypter.
148     *
149     * @param privateKey     The private EC key. Must not be {@code null}.
150     * @param publicKey      The public EC key. Must not be {@code null}.
151     * @param defCritHeaders The names of the critical header parameters
152     *                       that are deferred to the application for
153     *                       processing, empty set or {@code null} if none.
154     *
155     * @throws JOSEException If the elliptic curve is not supported.
156     */
157    public ECDH1PUDecrypter(final ECPrivateKey privateKey,
158                            final ECPublicKey publicKey,
159                            final Set<String> defCritHeaders)
160        throws JOSEException {
161
162        this(privateKey, publicKey, defCritHeaders, Curve.forECParameterSpec(privateKey.getParams()));
163    }
164
165
166    /**
167     * Creates a new Elliptic Curve Diffie-Hellman decrypter. This
168     * constructor can also accept a private EC key located in a PKCS#11
169     * store that doesn't expose the private key parameters (such as a
170     * smart card or HSM).
171     *
172     * @param privateKey     The private EC key. Must not be {@code null}.
173     * @param publicKey      The public EC key. Must not be {@code null}.
174     * @param defCritHeaders The names of the critical header parameters
175     *                       that are deferred to the application for
176     *                       processing, empty set or {@code null} if none.
177     * @param curve          The key curve. Must not be {@code null}.
178     *
179     * @throws JOSEException If the elliptic curve is not supported.
180     */
181    public ECDH1PUDecrypter(final ECPrivateKey privateKey,
182                            final ECPublicKey publicKey,
183                            final Set<String> defCritHeaders,
184                            final Curve curve)
185        throws JOSEException {
186
187        super(curve);
188
189        critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
190
191        this.privateKey = privateKey;
192        this.publicKey = publicKey;
193    }
194
195    /**
196     * Returns the public EC key.
197     *
198     * @return The public EC key.
199     */
200    public ECPublicKey getPublicKey() {
201
202        return publicKey;
203    }
204
205    /**
206     * Returns the private EC key.
207     *
208     * @return The private EC key. Casting to
209     *         {@link ECPrivateKey} may not be
210     *         possible if the key is located in a PKCS#11 store that
211     *         doesn't expose the private key parameters.
212     */
213    public PrivateKey getPrivateKey() {
214
215        return privateKey;
216    }
217
218
219    @Override
220    public Set<Curve> supportedEllipticCurves() {
221
222        return SUPPORTED_ELLIPTIC_CURVES;
223    }
224
225
226    @Override
227    public Set<String> getProcessedCriticalHeaderParams() {
228
229        return critPolicy.getProcessedCriticalHeaderParams();
230    }
231
232
233    @Override
234    public Set<String> getDeferredCriticalHeaderParams() {
235
236        return critPolicy.getProcessedCriticalHeaderParams();
237    }
238
239
240    @Override
241    public byte[] decrypt(final JWEHeader header,
242                          final Base64URL encryptedKey,
243                          final Base64URL iv,
244                          final Base64URL cipherText,
245                          final Base64URL authTag)
246        throws JOSEException {
247
248        ECDH1PU.validateSameCurve(privateKey, publicKey);
249
250        critPolicy.ensureHeaderPasses(header);
251
252        // Get ephemeral EC key
253        ECKey ephemeralKey = (ECKey) header.getEphemeralPublicKey();
254
255        if (ephemeralKey == null) {
256            throw new JOSEException("Missing ephemeral public EC key \"epk\" JWE header parameter");
257        }
258
259        ECPublicKey ephemeralPublicKey = ephemeralKey.toECPublicKey();
260
261        ECDH1PU.validateSameCurve(privateKey, ephemeralPublicKey);
262
263        SecretKey Ze = ECDH.deriveSharedSecret(
264            ephemeralPublicKey,
265            privateKey,
266            getJCAContext().getKeyEncryptionProvider());
267
268        SecretKey Zs = ECDH.deriveSharedSecret(
269                publicKey,
270                privateKey,
271                getJCAContext().getKeyEncryptionProvider());
272
273        SecretKey Z = ECDH1PU.deriveZ(Ze, Zs);
274
275        return decryptWithZ(header, Z, encryptedKey, iv, cipherText, authTag);
276    }
277}