001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2019, 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.impl;
019
020
021import java.util.Collections;
022import java.util.LinkedHashSet;
023import java.util.Set;
024
025import javax.crypto.SecretKey;
026
027import com.nimbusds.jose.EncryptionMethod;
028import com.nimbusds.jose.JOSEException;
029import com.nimbusds.jose.JWEAlgorithm;
030import com.nimbusds.jose.JWECryptoParts;
031import com.nimbusds.jose.JWEHeader;
032import com.nimbusds.jose.jwk.Curve;
033import com.nimbusds.jose.util.Base64URL;
034
035
036/**
037 * The base abstract class for Elliptic Curve Diffie-Hellman encrypters and
038 * decrypters of {@link com.nimbusds.jose.JWEObject JWE objects}.
039 *
040 * <p>Supports the following key management algorithms:
041 *
042 * <ul>
043 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES}
044 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW}
045 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW}
046 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW}
047 * </ul>
048 *
049 * <p>Supports the following elliptic curves:
050 *
051 * <ul>
052 *     <li>{@link com.nimbusds.jose.jwk.Curve#P_256}
053 *     <li>{@link com.nimbusds.jose.jwk.Curve#P_384}
054 *     <li>{@link com.nimbusds.jose.jwk.Curve#P_521}
055 *     <li>{@link com.nimbusds.jose.jwk.Curve#X25519}
056 * </ul>
057 *
058 * <p>Supports the following content encryption algorithms:
059 *
060 * <ul>
061 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
062 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
063 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
064 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
065 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
066 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
067 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
068 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
069 *     <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P}
070 * </ul>
071 *
072 * @author Tim McLean
073 * @author Vladimir Dzhuvinov
074 * @author Fernando González Callejas
075 * @version 2019-01-24
076 */
077public abstract class ECDHCryptoProvider extends BaseJWEProvider {
078
079
080        /**
081         * The supported JWE algorithms by the ECDH crypto provider class.
082         */
083        public static final Set<JWEAlgorithm> SUPPORTED_ALGORITHMS;
084
085
086        /**
087         * The supported encryption methods by the ECDH crypto provider class.
088         */
089        public static final Set<EncryptionMethod> SUPPORTED_ENCRYPTION_METHODS = ContentCryptoProvider.SUPPORTED_ENCRYPTION_METHODS;
090
091
092        static {
093                Set<JWEAlgorithm> algs = new LinkedHashSet<>();
094                algs.add(JWEAlgorithm.ECDH_ES);
095                algs.add(JWEAlgorithm.ECDH_ES_A128KW);
096                algs.add(JWEAlgorithm.ECDH_ES_A192KW);
097                algs.add(JWEAlgorithm.ECDH_ES_A256KW);
098                SUPPORTED_ALGORITHMS = Collections.unmodifiableSet(algs);
099        }
100
101
102        /**
103         * The elliptic curve.
104         */
105        private final Curve curve;
106
107
108        /**
109         * The Concatenation Key Derivation Function (KDF).
110         */
111        private final ConcatKDF concatKDF;
112
113
114        /**
115         * Creates a new Elliptic Curve Diffie-Hellman encryption /decryption
116         * provider.
117         *
118         * @param curve The elliptic curve. Must be supported and not
119         *              {@code null}.
120         *
121         * @throws JOSEException If the elliptic curve is not supported.
122         */
123        protected ECDHCryptoProvider(final Curve curve)
124                throws JOSEException {
125
126                super(SUPPORTED_ALGORITHMS, ContentCryptoProvider.SUPPORTED_ENCRYPTION_METHODS);
127
128                Curve definedCurve = curve != null ? curve : new Curve("unknown");
129
130                if (! supportedEllipticCurves().contains(curve)) {
131                        throw new JOSEException(AlgorithmSupportMessage.unsupportedEllipticCurve(
132                                definedCurve, supportedEllipticCurves()));
133                }
134
135                this.curve = curve;
136
137                concatKDF = new ConcatKDF("SHA-256");
138        }
139
140
141        /**
142         * Returns the Concatenation Key Derivation Function (KDF).
143         *
144         * @return The concat KDF.
145         */
146        protected ConcatKDF getConcatKDF() {
147
148                return concatKDF;
149        }
150
151
152        /**
153         * Returns the names of the supported elliptic curves. These correspond
154         * to the {@code crv} EC JWK parameter.
155         *
156         * @return The supported elliptic curves.
157         */
158        public abstract Set<Curve> supportedEllipticCurves();
159
160
161        /**
162         * Returns the elliptic curve of the key (JWK designation).
163         *
164         * @return The elliptic curve.
165         */
166        public Curve getCurve() {
167
168                return curve;
169        }
170
171        /**
172         * Encrypts the specified plaintext using the specified shared secret
173         * ("Z").
174         */
175        protected JWECryptoParts encryptWithZ(final JWEHeader header, final SecretKey Z, final byte[] clearText)
176                throws JOSEException {
177                
178                return this.encryptWithZ(header, Z, clearText, null);
179        }
180
181        /**
182         * Encrypts the specified plaintext using the specified shared secret
183         * ("Z") and, if provided, the content encryption key (CEK).
184         */
185        protected JWECryptoParts encryptWithZ(final JWEHeader header,
186                                              final SecretKey Z,
187                                              final byte[] clearText,
188                                              final SecretKey contentEncryptionKey)
189                throws JOSEException {
190
191                final JWEAlgorithm alg = header.getAlgorithm();
192                final ECDH.AlgorithmMode algMode = ECDH.resolveAlgorithmMode(alg);
193                final EncryptionMethod enc = header.getEncryptionMethod();
194
195                // Derive shared key via concat KDF
196                getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat
197                SecretKey sharedKey = ECDH.deriveSharedKey(header, Z, getConcatKDF());
198
199                final SecretKey cek;
200                final Base64URL encryptedKey; // The CEK encrypted (second JWE part)
201
202                if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) {
203                        cek = sharedKey;
204                        encryptedKey = null;
205                } else if (algMode.equals(ECDH.AlgorithmMode.KW)) {
206                        if(contentEncryptionKey != null) { // Use externally supplied CEK
207                                cek = contentEncryptionKey;
208                        } else { // Generate the CEK according to the enc method
209                                cek = ContentCryptoProvider.generateCEK(enc, getJCAContext().getSecureRandom());
210                        }
211                        encryptedKey = Base64URL.encode(AESKW.wrapCEK(cek, sharedKey, getJCAContext().getKeyEncryptionProvider()));
212                } else {
213                        throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode);
214                }
215
216                return ContentCryptoProvider.encrypt(header, clearText, cek, encryptedKey, getJCAContext());
217        }
218
219
220        /**
221         * Decrypts the encrypted JWE parts using the specified shared secret ("Z").
222         */
223        protected byte[] decryptWithZ(final JWEHeader header,
224                                      final SecretKey Z,
225                                      final Base64URL encryptedKey,
226                                      final Base64URL iv,
227                                      final Base64URL cipherText,
228                                      final Base64URL authTag)
229                throws JOSEException {
230
231                final JWEAlgorithm alg = header.getAlgorithm();
232                final ECDH.AlgorithmMode algMode = ECDH.resolveAlgorithmMode(alg);
233
234                // Derive shared key via concat KDF
235                getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat
236                SecretKey sharedKey = ECDH.deriveSharedKey(header, Z, getConcatKDF());
237
238                final SecretKey cek;
239
240                if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) {
241                        cek = sharedKey;
242                } else if (algMode.equals(ECDH.AlgorithmMode.KW)) {
243                        if (encryptedKey == null) {
244                                throw new JOSEException("Missing JWE encrypted key");
245                        }
246                        cek = AESKW.unwrapCEK(sharedKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider());
247                } else {
248                        throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode);
249                }
250
251                return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext());
252        }
253
254
255}