001package com.nimbusds.jose.crypto;
002
003
004import java.security.interfaces.ECPrivateKey;
005import java.security.interfaces.ECPublicKey;
006import java.util.Set;
007
008import javax.crypto.SecretKey;
009
010import com.nimbusds.jose.*;
011import com.nimbusds.jose.jwk.ECKey;
012import com.nimbusds.jose.util.Base64URL;
013
014
015/**
016 * Elliptic Curve Diffie-Hellman decrypter of
017 * {@link com.nimbusds.jose.JWEObject JWE objects}. Expects a private EC key
018 * (with a P-256, P-384 or P-521 curve).
019 *
020 * <p>See RFC 7518
021 * <a href="https://tools.ietf.org/html/rfc7518#section-4.6">section 4.6</a>
022 * for more information.
023 *
024 * <p>This class is thread-safe.
025 *
026 * <p>Supports the following key management algorithms:
027 *
028 * <ul>
029 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES}
030 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW}
031 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW}
032 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW}
033 * </ul>
034 *
035 * <p>Supports the following elliptic curves:
036 *
037 * <ul>
038 *     <li>{@link com.nimbusds.jose.jwk.ECKey.Curve#P_256}
039 *     <li>{@link com.nimbusds.jose.jwk.ECKey.Curve#P_384}
040 *     <li>{@link com.nimbusds.jose.jwk.ECKey.Curve#P_521}
041 * </ul>
042 *
043 * <p>Supports the following content encryption algorithms:
044 *
045 * <ul>
046 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
047 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
048 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
049 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
050 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
051 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
052 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
053 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
054 * </ul>
055 *
056 * @author Vladimir Dzhuvinov
057 * @version 2015-06-08
058 */
059public class ECDHDecrypter extends ECDHCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware {
060
061
062        /**
063         * The private EC key.
064         */
065        private final ECPrivateKey privateKey;
066
067
068        /**
069         * The critical header policy.
070         */
071        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
072
073
074        /**
075         * Creates a new Elliptic Curve Diffie-Hellman decrypter.
076         *
077         * @param privateKey The private EC key. Must not be {@code null}.
078         *
079         * @throws JOSEException If the elliptic curve is not supported.
080         */
081        public ECDHDecrypter(final ECPrivateKey privateKey)
082                throws JOSEException {
083
084                this(privateKey, null);
085        }
086
087
088        /**
089         * Creates a new Elliptic Curve Diffie-Hellman decrypter.
090         *
091         * @param ecJWK The EC JSON Web Key (JWK). Must contain a private
092         *              part. Must not be {@code null}.
093         *
094         * @throws JOSEException If the elliptic curve is not supported.
095         */
096        public ECDHDecrypter(final ECKey ecJWK)
097                throws JOSEException {
098
099                super(ecJWK.getCurve());
100
101                if (! ecJWK.isPrivate()) {
102                        throw new JOSEException("The EC JWK doesn't contain a private part");
103                }
104
105                this.privateKey = ecJWK.toECPrivateKey();
106        }
107
108
109        /**
110         * Creates a new Elliptic Curve Diffie-Hellman decrypter.
111         *
112         * @param privateKey     The private EC key. Must not be {@code null}.
113         * @param defCritHeaders The names of the critical header parameters
114         *                       that are deferred to the application for
115         *                       processing, empty set or {@code null} if none.
116         *
117         * @throws JOSEException If the elliptic curve is not supported.
118         */
119        public ECDHDecrypter(final ECPrivateKey privateKey, final Set<String> defCritHeaders)
120                throws JOSEException {
121
122                super(ECKey.Curve.forECParameterSpec(privateKey.getParams()));
123
124                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
125
126                this.privateKey = privateKey;
127        }
128
129
130        /**
131         * Returns the private EC key.
132         *
133         * @return The private EC key.
134         */
135        public ECPrivateKey getPrivateKey() {
136
137                return privateKey;
138        }
139
140
141        @Override
142        public Set<String> getProcessedCriticalHeaderParams() {
143
144                return critPolicy.getProcessedCriticalHeaderParams();
145        }
146
147
148        @Override
149        public Set<String> getDeferredCriticalHeaderParams() {
150
151                return critPolicy.getProcessedCriticalHeaderParams();
152        }
153
154
155        @Override
156        public byte[] decrypt(final JWEHeader header,
157                              final Base64URL encryptedKey,
158                              final Base64URL iv,
159                              final Base64URL cipherText,
160                              final Base64URL authTag)
161                throws JOSEException {
162
163                final JWEAlgorithm alg = header.getAlgorithm();
164                final ECDH.AlgorithmMode algMode = ECDH.resolveAlgorithmMode(alg);
165
166                critPolicy.ensureHeaderPasses(header);
167
168                // Get ephemeral EC key
169                ECKey ephemeralKey = header.getEphemeralPublicKey();
170
171                if (ephemeralKey == null) {
172                        throw new JOSEException("Missing ephemeral public EC key \"epk\" JWE header parameter");
173                }
174
175                ECPublicKey ephemeralPublicKey = ephemeralKey.toECPublicKey();
176
177                // Derive 'Z'
178                SecretKey Z = ECDH.deriveSharedSecret(
179                        ephemeralPublicKey,
180                        privateKey,
181                        getJCAContext().getKeyEncryptionProvider());
182
183                // Derive shared key via concat KDF
184                getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat
185                SecretKey sharedKey = ECDH.deriveSharedKey(header, Z, getConcatKDF());
186
187                final SecretKey cek;
188
189                if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) {
190                        cek = sharedKey;
191                } else if (algMode.equals(ECDH.AlgorithmMode.KW)) {
192                        if (encryptedKey == null) {
193                                throw new JOSEException("Missing JWE encrypted key");
194                        }
195                        cek = AESKW.unwrapCEK(sharedKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider());
196                } else {
197                        throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode);
198                }
199
200                return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext());
201        }
202}