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.*;
022import java.security.interfaces.ECPrivateKey;
023import java.security.interfaces.ECPublicKey;
024import java.security.spec.ECParameterSpec;
025import java.util.Collections;
026import java.util.LinkedHashSet;
027import java.util.Set;
028import javax.crypto.SecretKey;
029
030import com.nimbusds.jose.*;
031import com.nimbusds.jose.jwk.Curve;
032import com.nimbusds.jose.jwk.ECKey;
033import net.jcip.annotations.ThreadSafe;
034
035
036/**
037 * Elliptic Curve Diffie-Hellman encrypter of
038 * {@link com.nimbusds.jose.JWEObject JWE objects} for curves using EC JWK keys.
039 * Expects a public EC key (with a P-256, P-384 or P-521 curve).
040 *
041 * <p>See RFC 7518
042 * <a href="https://tools.ietf.org/html/rfc7518#section-4.6">section 4.6</a>
043 * for more information.
044 *
045 * <p>For Curve25519/X25519, see {@link X25519Encrypter} instead.
046 *
047 * <p>This class is thread-safe.
048 *
049 * <p>Supports the following key management algorithms:
050 *
051 * <ul>
052 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES}
053 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW}
054 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW}
055 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW}
056 * </ul>
057 *
058 * <p>Supports the following elliptic curves:
059 *
060 * <ul>
061 *     <li>{@link com.nimbusds.jose.jwk.Curve#P_256}
062 *     <li>{@link com.nimbusds.jose.jwk.Curve#P_384}
063 *     <li>{@link com.nimbusds.jose.jwk.Curve#P_521}
064 * </ul>
065 *
066 * <p>Supports the following content encryption algorithms:
067 *
068 * <ul>
069 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
070 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
071 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
072 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
073 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
074 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
075 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
076 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
077 * </ul>
078 *
079 * @author Tim McLean
080 * @author Vladimir Dzhuvinov
081 * @version 2018-07-12
082 */
083@ThreadSafe
084public class ECDHEncrypter extends ECDHCryptoProvider implements JWEEncrypter {
085
086
087        /**
088         * The supported EC JWK curves by the ECDH crypto provider class.
089         */
090        public static final Set<Curve> SUPPORTED_ELLIPTIC_CURVES;
091
092
093        static {
094                Set<Curve> curves = new LinkedHashSet<>();
095                curves.add(Curve.P_256);
096                curves.add(Curve.P_384);
097                curves.add(Curve.P_521);
098                SUPPORTED_ELLIPTIC_CURVES = Collections.unmodifiableSet(curves);
099        }
100
101
102        /**
103         * The public EC key.
104         */
105        private final ECPublicKey publicKey;
106
107
108        /**
109         * Creates a new Elliptic Curve Diffie-Hellman encrypter.
110         *
111         * @param publicKey The public EC key. Must not be {@code null}.
112         *
113         * @throws JOSEException If the elliptic curve is not supported.
114         */
115        public ECDHEncrypter(final ECPublicKey publicKey)
116                throws JOSEException {
117
118                super(Curve.forECParameterSpec(publicKey.getParams()));
119
120                this.publicKey = publicKey;
121        }
122
123
124        /**
125         * Creates a new Elliptic Curve Diffie-Hellman encrypter.
126         *
127         * @param ecJWK The EC JSON Web Key (JWK). Must not be {@code null}.
128         *
129         * @throws JOSEException If the elliptic curve is not supported.
130         */
131        public ECDHEncrypter(final ECKey ecJWK)
132                throws JOSEException {
133
134                super(ecJWK.getCurve());
135
136                publicKey = ecJWK.toECPublicKey();
137        }
138
139
140        /**
141         * Returns the public EC key.
142         *
143         * @return The public EC key.
144         */
145        public ECPublicKey getPublicKey() {
146
147                return publicKey;
148        }
149
150
151        @Override
152        public Set<Curve> supportedEllipticCurves() {
153
154                return SUPPORTED_ELLIPTIC_CURVES;
155        }
156
157
158        @Override
159        public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText)
160                throws JOSEException {
161
162                // Generate ephemeral EC key pair on the same curve as the consumer's public key
163                KeyPair ephemeralKeyPair = generateEphemeralKeyPair(publicKey.getParams());
164                ECPublicKey ephemeralPublicKey = (ECPublicKey)ephemeralKeyPair.getPublic();
165                ECPrivateKey ephemeralPrivateKey = (ECPrivateKey)ephemeralKeyPair.getPrivate();
166
167                // Add the ephemeral public EC key to the header
168                JWEHeader updatedHeader = new JWEHeader.Builder(header).
169                        ephemeralPublicKey(new ECKey.Builder(getCurve(), ephemeralPublicKey).build()).
170                        build();
171
172                // Derive 'Z'
173                SecretKey Z = ECDH.deriveSharedSecret(
174                        publicKey,
175                        ephemeralPrivateKey,
176                        getJCAContext().getKeyEncryptionProvider());
177
178                return encryptWithZ(updatedHeader, Z, clearText);
179        }
180
181
182        /**
183         * Generates a new ephemeral EC key pair with the specified curve.
184         *
185         * @param ecParameterSpec The EC key spec. Must not be {@code null}.
186         *
187         * @return The EC key pair.
188         *
189         * @throws JOSEException If the EC key pair couldn't be generated.
190         */
191        private KeyPair generateEphemeralKeyPair(final ECParameterSpec ecParameterSpec)
192                throws JOSEException {
193
194                Provider keProvider = getJCAContext().getKeyEncryptionProvider();
195
196                try {
197                        KeyPairGenerator generator;
198
199                        if (keProvider != null) {
200                                generator = KeyPairGenerator.getInstance("EC", keProvider);
201                        } else {
202                                generator = KeyPairGenerator.getInstance("EC");
203                        }
204
205                        generator.initialize(ecParameterSpec);
206                        return generator.generateKeyPair();
207                } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
208                        throw new JOSEException("Couldn't generate ephemeral EC key pair: " + e.getMessage(), e);
209                }
210        }
211}