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}