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