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