001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2019, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.crypto; 019 020 021import java.security.InvalidAlgorithmParameterException; 022import java.security.KeyPair; 023import java.security.KeyPairGenerator; 024import java.security.NoSuchAlgorithmException; 025import java.security.Provider; 026import java.security.interfaces.ECPrivateKey; 027import java.security.interfaces.ECPublicKey; 028import java.security.spec.ECParameterSpec; 029import java.util.Arrays; 030import java.util.Collections; 031import java.util.LinkedHashSet; 032import java.util.Set; 033 034import javax.crypto.SecretKey; 035 036import com.nimbusds.jose.*; 037import com.nimbusds.jose.crypto.impl.AAD; 038import com.nimbusds.jose.crypto.impl.ECDH; 039import com.nimbusds.jose.crypto.impl.ECDHCryptoProvider; 040import com.nimbusds.jose.jwk.Curve; 041import com.nimbusds.jose.jwk.ECKey; 042 043import net.jcip.annotations.ThreadSafe; 044 045 046/** 047 * Elliptic Curve Diffie-Hellman encrypter of 048 * {@link com.nimbusds.jose.JWEObject JWE objects} for curves using EC JWK keys. 049 * Expects a public EC key (with a P-256, P-384 or P-521 curve). 050 * 051 * <p>See RFC 7518 052 * <a href="https://tools.ietf.org/html/rfc7518#section-4.6">section 4.6</a> 053 * for more information. 054 * 055 * <p>For Curve25519/X25519, see {@link X25519Encrypter} instead. 056 * 057 * <p>This class is thread-safe. 058 * 059 * <p>Supports the following key management algorithms: 060 * 061 * <ul> 062 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES} 063 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW} 064 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW} 065 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW} 066 * </ul> 067 * 068 * <p>Supports the following elliptic curves: 069 * 070 * <ul> 071 * <li>{@link com.nimbusds.jose.jwk.Curve#P_256} 072 * <li>{@link com.nimbusds.jose.jwk.Curve#P_384} 073 * <li>{@link com.nimbusds.jose.jwk.Curve#P_521} 074 * </ul> 075 * 076 * <p>Supports the following content encryption algorithms: 077 * 078 * <ul> 079 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 080 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 081 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 082 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 083 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 084 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 085 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 086 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 087 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 088 * </ul> 089 * 090 * @author Tim McLean 091 * @author Vladimir Dzhuvinov 092 * @author Fernando González Callejas 093 * @author Egor Puzanov 094 * @version 2023-05-17 095 */ 096@ThreadSafe 097public class ECDHEncrypter extends ECDHCryptoProvider implements JWEEncrypter { 098 099 100 /** 101 * The supported EC JWK curves by the ECDH crypto provider class. 102 */ 103 public static final Set<Curve> SUPPORTED_ELLIPTIC_CURVES; 104 105 106 static { 107 Set<Curve> curves = new LinkedHashSet<>(); 108 curves.add(Curve.P_256); 109 curves.add(Curve.P_384); 110 curves.add(Curve.P_521); 111 SUPPORTED_ELLIPTIC_CURVES = Collections.unmodifiableSet(curves); 112 } 113 114 115 /** 116 * The public EC key. 117 */ 118 private final ECPublicKey publicKey; 119 120 /** 121 * Creates a new Elliptic Curve Diffie-Hellman encrypter. 122 * 123 * @param publicKey The public EC key. Must not be {@code null}. 124 * 125 * @throws JOSEException If the elliptic curve is not supported. 126 */ 127 public ECDHEncrypter(final ECPublicKey publicKey) 128 throws JOSEException { 129 130 this(publicKey, null); 131 } 132 133 134 /** 135 * Creates a new Elliptic Curve Diffie-Hellman encrypter. 136 * 137 * @param ecJWK The EC JSON Web Key (JWK). Must not be {@code null}. 138 * 139 * @throws JOSEException If the elliptic curve is not supported. 140 */ 141 public ECDHEncrypter(final ECKey ecJWK) throws 142 JOSEException { 143 144 this(ecJWK.toECPublicKey(), null); 145 } 146 147 /** 148 * Creates a new Elliptic Curve Diffie-Hellman encrypter with an 149 * optionally specified content encryption key (CEK). 150 * 151 * @param publicKey The public EC key. Must not be 152 * {@code null}. 153 * @param contentEncryptionKey The content encryption key (CEK) to use. 154 * If specified its algorithm must be "AES" 155 * and its length must match the expected 156 * for the JWE encryption method ("enc"). 157 * If {@code null} a CEK will be generated 158 * for each JWE. 159 * @throws JOSEException If the elliptic curve is not supported. 160 */ 161 public ECDHEncrypter(final ECPublicKey publicKey, final SecretKey contentEncryptionKey) 162 throws JOSEException { 163 164 super(Curve.forECParameterSpec(publicKey.getParams()), contentEncryptionKey); 165 166 this.publicKey = publicKey; 167 } 168 169 170 /** 171 * Returns the public EC key. 172 * 173 * @return The public EC key. 174 */ 175 public ECPublicKey getPublicKey() { 176 177 return publicKey; 178 } 179 180 181 @Override 182 public Set<Curve> supportedEllipticCurves() { 183 184 return SUPPORTED_ELLIPTIC_CURVES; 185 } 186 187 188 /** 189 * Encrypts the specified clear text of a {@link JWEObject JWE object}. 190 * 191 * @param header The JSON Web Encryption (JWE) header. Must specify 192 * a supported JWE algorithm and method. Must not be 193 * {@code null}. 194 * @param clearText The clear text to encrypt. Must not be {@code null}. 195 * 196 * @return The resulting JWE crypto parts. 197 * 198 * @throws JOSEException If the JWE algorithm or method is not 199 * supported or if encryption failed for some 200 * other internal reason. 201 */ 202 @Deprecated 203 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 204 throws JOSEException { 205 206 return encrypt(header, clearText, AAD.compute(header)); 207 } 208 209 210 @Override 211 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText, final byte[] aad) 212 throws JOSEException { 213 214 // Generate ephemeral EC key pair on the same curve as the consumer's public key 215 KeyPair ephemeralKeyPair = generateEphemeralKeyPair(publicKey.getParams()); 216 ECPublicKey ephemeralPublicKey = (ECPublicKey)ephemeralKeyPair.getPublic(); 217 ECPrivateKey ephemeralPrivateKey = (ECPrivateKey)ephemeralKeyPair.getPrivate(); 218 219 // Add the ephemeral public EC key to the header 220 JWEHeader updatedHeader = new JWEHeader.Builder(header). 221 ephemeralPublicKey(new ECKey.Builder(getCurve(), ephemeralPublicKey).build()). 222 build(); 223 224 // Derive 'Z' 225 SecretKey Z = ECDH.deriveSharedSecret( 226 publicKey, 227 ephemeralPrivateKey, 228 getJCAContext().getKeyEncryptionProvider()); 229 230 // for JWEObject we need update the AAD as well 231 final byte[] updatedAAD = Arrays.equals(AAD.compute(header), aad) ? AAD.compute(updatedHeader) : aad; 232 233 return encryptWithZ(updatedHeader, Z, clearText, updatedAAD); 234 } 235 236 237 /** 238 * Generates a new ephemeral EC key pair with the specified curve. 239 * 240 * @param ecParameterSpec The EC key spec. Must not be {@code null}. 241 * 242 * @return The EC key pair. 243 * 244 * @throws JOSEException If the EC key pair couldn't be generated. 245 */ 246 private KeyPair generateEphemeralKeyPair(final ECParameterSpec ecParameterSpec) 247 throws JOSEException { 248 249 Provider keProvider = getJCAContext().getKeyEncryptionProvider(); 250 251 try { 252 KeyPairGenerator generator; 253 254 if (keProvider != null) { 255 generator = KeyPairGenerator.getInstance("EC", keProvider); 256 } else { 257 generator = KeyPairGenerator.getInstance("EC"); 258 } 259 260 generator.initialize(ecParameterSpec); 261 return generator.generateKeyPair(); 262 } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { 263 throw new JOSEException("Couldn't generate ephemeral EC key pair: " + e.getMessage(), e); 264 } 265 } 266}