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 *     <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P}
084 * </ul>
085 *
086 * @author Vladimir Dzhuvinov
087 * @version 2018-12-12
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         * Creates a new Elliptic Curve Diffie-Hellman decrypter.
121         *
122         * @param privateKey The private EC key. Must not be {@code null}.
123         *
124         * @throws JOSEException If the elliptic curve is not supported.
125         */
126        public ECDHDecrypter(final ECPrivateKey privateKey)
127                throws JOSEException {
128
129                this(privateKey, null);
130        }
131
132
133        /**
134         * Creates a new Elliptic Curve Diffie-Hellman decrypter.
135         *
136         * @param ecJWK The EC JSON Web Key (JWK). Must contain a private
137         *              part. Must not be {@code null}.
138         *
139         * @throws JOSEException If the elliptic curve is not supported.
140         */
141        public ECDHDecrypter(final ECKey ecJWK)
142                throws JOSEException {
143
144                super(ecJWK.getCurve());
145
146                if (! ecJWK.isPrivate()) {
147                        throw new JOSEException("The EC JWK doesn't contain a private part");
148                }
149
150                this.privateKey = ecJWK.toECPrivateKey();
151        }
152
153
154        /**
155         * Creates a new Elliptic Curve Diffie-Hellman decrypter.
156         *
157         * @param privateKey     The private EC key. Must not be {@code null}.
158         * @param defCritHeaders The names of the critical header parameters
159         *                       that are deferred to the application for
160         *                       processing, empty set or {@code null} if none.
161         *
162         * @throws JOSEException If the elliptic curve is not supported.
163         */
164        public ECDHDecrypter(final ECPrivateKey privateKey, final Set<String> defCritHeaders)
165                throws JOSEException {
166
167                this(privateKey, defCritHeaders, Curve.forECParameterSpec(privateKey.getParams()));
168        }
169
170
171        /**
172         * Creates a new Elliptic Curve Diffie-Hellman decrypter. This
173         * constructor can also accept a private EC key located in a PKCS#11
174         * store that doesn't expose the private key parameters (such as a
175         * smart card or HSM).
176         *
177         * @param privateKey     The private EC key. Must not be {@code null}.
178         * @param defCritHeaders The names of the critical header parameters
179         *                       that are deferred to the application for
180         *                       processing, empty set or {@code null} if none.
181         * @param curve          The key curve. Must not be {@code null}.
182         *
183         * @throws JOSEException If the elliptic curve is not supported.
184         */
185        public ECDHDecrypter(final PrivateKey privateKey,
186                             final Set<String> defCritHeaders,
187                             final Curve curve)
188                throws JOSEException {
189
190                super(curve);
191
192                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
193
194                this.privateKey = privateKey;
195        }
196
197
198        /**
199         * Returns the private EC key.
200         *
201         * @return The private EC key. Casting to
202         *         {@link java.security.interfaces.ECPrivateKey} may not be
203         *         possible if the key is located in a PKCS#11 store that
204         *         doesn't expose the private key parameters.
205         */
206        public PrivateKey getPrivateKey() {
207
208                return privateKey;
209        }
210
211
212        @Override
213        public Set<Curve> supportedEllipticCurves() {
214
215                return SUPPORTED_ELLIPTIC_CURVES;
216        }
217
218
219        @Override
220        public Set<String> getProcessedCriticalHeaderParams() {
221
222                return critPolicy.getProcessedCriticalHeaderParams();
223        }
224
225
226        @Override
227        public Set<String> getDeferredCriticalHeaderParams() {
228
229                return critPolicy.getProcessedCriticalHeaderParams();
230        }
231
232
233        @Override
234        public byte[] decrypt(final JWEHeader header,
235                              final Base64URL encryptedKey,
236                              final Base64URL iv,
237                              final Base64URL cipherText,
238                              final Base64URL authTag)
239                throws JOSEException {
240
241                critPolicy.ensureHeaderPasses(header);
242
243                // Get ephemeral EC key
244                ECKey ephemeralKey = (ECKey) header.getEphemeralPublicKey();
245
246                if (ephemeralKey == null) {
247                        throw new JOSEException("Missing ephemeral public EC key \"epk\" JWE header parameter");
248                }
249
250                ECPublicKey ephemeralPublicKey = ephemeralKey.toECPublicKey();
251                
252                // Curve check
253                if (getPrivateKey() instanceof ECPrivateKey) {
254                        ECPrivateKey ecPrivateKey = (ECPrivateKey)getPrivateKey();
255                        if (!ECChecks.isPointOnCurve(ephemeralPublicKey, ecPrivateKey)) {
256                                throw new JOSEException("Invalid ephemeral public EC key: Point(s) not on the expected curve");
257                        }
258                } else {
259                        if (!ECChecks.isPointOnCurve(ephemeralPublicKey, getCurve().toECParameterSpec())) {
260                                throw new JOSEException("Invalid ephemeral public EC key: Point(s) not on the expected curve");
261                        }
262                }
263
264                // Derive 'Z'
265                SecretKey Z = ECDH.deriveSharedSecret(
266                        ephemeralPublicKey,
267                        privateKey,
268                        getJCAContext().getKeyEncryptionProvider());
269
270                return decryptWithZ(header, Z, encryptedKey, iv, cipherText, authTag);
271        }
272}