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