001package com.nimbusds.jose.crypto; 002 003 004import java.security.*; 005import java.security.interfaces.ECPrivateKey; 006import java.security.interfaces.ECPublicKey; 007import java.security.spec.ECParameterSpec; 008 009import javax.crypto.SecretKey; 010 011import net.jcip.annotations.ThreadSafe; 012 013import com.nimbusds.jose.*; 014import com.nimbusds.jose.jwk.ECKey; 015import com.nimbusds.jose.util.Base64URL; 016 017 018/** 019 * Elliptic Curve Diffie-Hellman encrypter of 020 * {@link com.nimbusds.jose.JWEObject JWE objects}. Expects a public EC key 021 * (with a P-256, P-384 or P-521 curve). 022 * 023 * <p>See RFC 7518 024 * <a href="https://tools.ietf.org/html/rfc7518#section-4.6">section 4.6</a> 025 * for more information. 026 * 027 * <p>This class is thread-safe. 028 * 029 * <p>Supports the following key management algorithms: 030 * 031 * <ul> 032 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES} 033 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW} 034 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW} 035 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW} 036 * </ul> 037 * 038 * <p>Supports the following elliptic curves: 039 * 040 * <ul> 041 * <li>{@link com.nimbusds.jose.jwk.ECKey.Curve#P_256} 042 * <li>{@link com.nimbusds.jose.jwk.ECKey.Curve#P_384} 043 * <li>{@link com.nimbusds.jose.jwk.ECKey.Curve#P_521} 044 * </ul> 045 * 046 * <p>Supports the following content encryption algorithms: 047 * 048 * <ul> 049 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 050 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 051 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 052 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 053 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 054 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 055 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 056 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 057 * </ul> 058 * 059 * @author Vladimir Dzhuvinov 060 * @version 2015-06-08 061 */ 062@ThreadSafe 063public class ECDHEncrypter extends ECDHCryptoProvider implements JWEEncrypter { 064 065 066 /** 067 * The public EC key. 068 */ 069 private final ECPublicKey publicKey; 070 071 072 /** 073 * Creates a new Elliptic Curve Diffie-Hellman encrypter. 074 * 075 * @param publicKey The public EC key. Must not be {@code null}. 076 * 077 * @throws JOSEException If the elliptic curve is not supported. 078 */ 079 public ECDHEncrypter(final ECPublicKey publicKey) 080 throws JOSEException { 081 082 super(ECKey.Curve.forECParameterSpec(publicKey.getParams())); 083 084 this.publicKey = publicKey; 085 } 086 087 088 /** 089 * Creates a new Elliptic Curve Diffie-Hellman encrypter. 090 * 091 * @param ecJWK The EC JSON Web Key (JWK). Must not be {@code null}. 092 * 093 * @throws JOSEException If the elliptic curve is not supported. 094 */ 095 public ECDHEncrypter(final ECKey ecJWK) 096 throws JOSEException { 097 098 super(ecJWK.getCurve()); 099 100 publicKey = ecJWK.toECPublicKey(); 101 } 102 103 104 /** 105 * Returns the public EC key. 106 * 107 * @return The public EC key. 108 */ 109 public ECPublicKey getPublicKey() { 110 111 return publicKey; 112 } 113 114 115 @Override 116 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 117 throws JOSEException { 118 119 final JWEAlgorithm alg = header.getAlgorithm(); 120 final ECDH.AlgorithmMode algMode = ECDH.resolveAlgorithmMode(alg); 121 final EncryptionMethod enc = header.getEncryptionMethod(); 122 123 // Generate ephemeral EC key pair on the same curve as the consumer's public key 124 KeyPair ephemeralKeyPair = generateEphemeralKeyPair(publicKey.getParams()); 125 ECPublicKey ephemeralPublicKey = (ECPublicKey)ephemeralKeyPair.getPublic(); 126 ECPrivateKey ephemeralPrivateKey = (ECPrivateKey)ephemeralKeyPair.getPrivate(); 127 128 // Derive 'Z' 129 SecretKey Z = ECDH.deriveSharedSecret( 130 publicKey, 131 ephemeralPrivateKey, 132 getJCAContext().getKeyEncryptionProvider()); 133 134 // Derive shared key via concat KDF 135 getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat 136 SecretKey sharedKey = ECDH.deriveSharedKey(header, Z, getConcatKDF()); 137 138 final SecretKey cek; 139 final Base64URL encryptedKey; // The CEK encrypted (second JWE part) 140 141 if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) { 142 cek = sharedKey; 143 encryptedKey = null; 144 } else if (algMode.equals(ECDH.AlgorithmMode.KW)) { 145 cek = ContentCryptoProvider.generateCEK(enc, getJCAContext().getSecureRandom()); 146 encryptedKey = Base64URL.encode(AESKW.wrapCEK(cek, sharedKey, getJCAContext().getKeyEncryptionProvider())); 147 } else { 148 throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode); 149 } 150 151 // Add the ephemeral public EC key to the header 152 JWEHeader updatedHeader = new JWEHeader.Builder(header). 153 ephemeralPublicKey(new ECKey.Builder(getCurve(), ephemeralPublicKey).build()). 154 build(); 155 156 return ContentCryptoProvider.encrypt(updatedHeader, clearText, cek, encryptedKey, getJCAContext()); 157 } 158 159 160 /** 161 * Generates a new ephemeral EC key pair with the specified curve. 162 * 163 * @param ecParameterSpec The EC key spec. Must not be {@code null}. 164 * 165 * @return The EC key pair. 166 * 167 * @throws JOSEException If the EC key pair couldn't be generated. 168 */ 169 private KeyPair generateEphemeralKeyPair(final ECParameterSpec ecParameterSpec) 170 throws JOSEException { 171 172 Provider keProvider = getJCAContext().getKeyEncryptionProvider(); 173 174 try { 175 KeyPairGenerator generator; 176 177 if (keProvider != null) { 178 generator = KeyPairGenerator.getInstance("EC", keProvider); 179 } else { 180 generator = KeyPairGenerator.getInstance("EC"); 181 } 182 183 generator.initialize(ecParameterSpec); 184 return generator.generateKeyPair(); 185 } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { 186 throw new JOSEException("Couldn't generate ephemeral EC key pair: " + e.getMessage(), e); 187 } 188 } 189}