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.CriticalHeaderParamsAware;
030import com.nimbusds.jose.JOSEException;
031import com.nimbusds.jose.JWEDecrypter;
032import com.nimbusds.jose.JWEHeader;
033import com.nimbusds.jose.crypto.impl.CriticalHeaderParamsDeferral;
034import com.nimbusds.jose.crypto.impl.ECDH;
035import com.nimbusds.jose.crypto.impl.ECDHCryptoProvider;
036import com.nimbusds.jose.crypto.utils.ECChecks;
037import com.nimbusds.jose.jwk.Curve;
038import com.nimbusds.jose.jwk.ECKey;
039import com.nimbusds.jose.util.Base64URL;
040
041
042/**
043 * Elliptic Curve Diffie-Hellman decrypter of
044 * {@link com.nimbusds.jose.JWEObject JWE objects} for curves using EC JWK
045 * keys. Expects a private EC key (with a P-256, P-384 or P-521 curve).
046 *
047 * <p>See RFC 7518
048 * <a href="https://tools.ietf.org/html/rfc7518#section-4.6">section 4.6</a>
049 * for more information.
050 *
051 * <p>For Curve25519/X25519, see {@link X25519Decrypter} instead.
052 *
053 * <p>This class is thread-safe.
054 *
055 * <p>Supports the following key management algorithms:
056 *
057 * <ul>
058 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES}
059 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW}
060 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW}
061 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW}
062 * </ul>
063 *
064 * <p>Supports the following elliptic curves:
065 *
066 * <ul>
067 *     <li>{@link com.nimbusds.jose.jwk.Curve#P_256}
068 *     <li>{@link com.nimbusds.jose.jwk.Curve#P_384}
069 *     <li>{@link com.nimbusds.jose.jwk.Curve#P_521}
070 * </ul>
071 *
072 * <p>Supports the following content encryption algorithms:
073 *
074 * <ul>
075 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
076 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
077 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
078 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
079 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
080 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
081 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
082 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
083 * </ul>
084 *
085 * @author Vladimir Dzhuvinov
086 * @version 2018-12-12
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());
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);
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        @Override
233        public byte[] decrypt(final JWEHeader header,
234                              final Base64URL encryptedKey,
235                              final Base64URL iv,
236                              final Base64URL cipherText,
237                              final Base64URL authTag)
238                throws JOSEException {
239
240                critPolicy.ensureHeaderPasses(header);
241
242                // Get ephemeral EC key
243                ECKey ephemeralKey = (ECKey) header.getEphemeralPublicKey();
244
245                if (ephemeralKey == null) {
246                        throw new JOSEException("Missing ephemeral public EC key \"epk\" JWE header parameter");
247                }
248
249                ECPublicKey ephemeralPublicKey = ephemeralKey.toECPublicKey();
250                
251                // Curve check
252                if (getPrivateKey() instanceof ECPrivateKey) {
253                        ECPrivateKey ecPrivateKey = (ECPrivateKey)getPrivateKey();
254                        if (!ECChecks.isPointOnCurve(ephemeralPublicKey, ecPrivateKey)) {
255                                throw new JOSEException("Invalid ephemeral public EC key: Point(s) not on the expected curve");
256                        }
257                } else {
258                        if (!ECChecks.isPointOnCurve(ephemeralPublicKey, getCurve().toECParameterSpec())) {
259                                throw new JOSEException("Invalid ephemeral public EC key: Point(s) not on the expected curve");
260                        }
261                }
262
263                // Derive 'Z'
264                SecretKey Z = ECDH.deriveSharedSecret(
265                        ephemeralPublicKey,
266                        privateKey,
267                        getJCAContext().getKeyEncryptionProvider());
268
269                return decryptWithZ(header, Z, encryptedKey, iv, cipherText, authTag);
270        }
271}