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