001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2021, Connect2id Ltd and contributors. 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 com.nimbusds.jose.*; 022import com.nimbusds.jose.crypto.impl.AAD; 023import com.nimbusds.jose.crypto.impl.ECDH1PU; 024import com.nimbusds.jose.crypto.impl.ECDH1PUCryptoProvider; 025import com.nimbusds.jose.jwk.Curve; 026import com.nimbusds.jose.jwk.ECKey; 027import net.jcip.annotations.ThreadSafe; 028 029import javax.crypto.SecretKey; 030import java.security.*; 031import java.security.interfaces.ECPrivateKey; 032import java.security.interfaces.ECPublicKey; 033import java.security.spec.ECParameterSpec; 034import java.util.Arrays; 035import java.util.Collections; 036import java.util.LinkedHashSet; 037import java.util.Set; 038 039 040/** 041 * Elliptic Curve Diffie-Hellman encrypter of 042 * {@link com.nimbusds.jose.JWEObject JWE objects} for curves using an EC JWK. 043 * Expects a public EC key (with a P-256, P-384, or P-521 curve). 044 * 045 * <p>Public Key Authenticated Encryption for JOSE 046 * <a href="https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04">ECDH-1PU</a> 047 * for more information. 048 * 049 * <p>For Curve25519/X25519, see {@link ECDH1PUX25519Encrypter} instead. 050 * 051 * <p>This class is thread-safe. 052 * 053 * <p>Supports the following key management algorithms: 054 * 055 * <ul> 056 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU} 057 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU_A128KW} 058 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU_A192KW} 059 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU_A256KW} 060 * </ul> 061 * 062 * <p>Supports the following elliptic curves: 063 * 064 * <ul> 065 * <li>{@link Curve#P_256} 066 * <li>{@link Curve#P_384} 067 * <li>{@link Curve#P_521} 068 * </ul> 069 * 070 * <p>Supports the following content encryption algorithms for Direct key agreement mode: 071 * 072 * <ul> 073 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 074 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 075 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 076 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 077 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 078 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 079 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 080 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 081 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 082 * </ul> 083 * 084 * <p>Supports the following content encryption algorithms for Key wrapping mode: 085 * 086 * <ul> 087 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 088 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 089 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 090 * </ul> 091 * 092 * @author Alexander Martynov 093 * @author Egor Puzanov 094 * @version 2023-05-17 095 */ 096@ThreadSafe 097public class ECDH1PUEncrypter extends ECDH1PUCryptoProvider 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 * The private EC key; 122 */ 123 private final ECPrivateKey privateKey; 124 125 /** 126 * Creates a new Elliptic Curve Diffie-Hellman encrypter. 127 * 128 * @param privateKey The private EC key. Must not be {@code null}. 129 * @param publicKey The public EC key. Must not be {@code null}. 130 * 131 * @throws JOSEException If the elliptic curve is not supported. 132 */ 133 public ECDH1PUEncrypter(final ECPrivateKey privateKey, final ECPublicKey publicKey) 134 throws JOSEException { 135 136 this(privateKey, publicKey, null); 137 } 138 139 140 /** 141 * Creates a new Elliptic Curve Diffie-Hellman encrypter with an 142 * optionally specified content encryption key (CEK). 143 * 144 * @param privateKey The private EC key. Must not be 145 * {@code null}. 146 * @param publicKey The public EC key. Must not be 147 * {@code null}. 148 * @param contentEncryptionKey The content encryption key (CEK) to use. 149 * If specified its algorithm must be "AES" 150 * and its length must match the expected 151 * for the JWE encryption method ("enc"). 152 * If {@code null} a CEK will be generated 153 * for each JWE. 154 * @throws JOSEException If the elliptic curve is not supported. 155 */ 156 public ECDH1PUEncrypter(final ECPrivateKey privateKey, 157 final ECPublicKey publicKey, 158 final SecretKey contentEncryptionKey) 159 throws JOSEException { 160 161 super(Curve.forECParameterSpec(publicKey.getParams()), contentEncryptionKey); 162 163 this.privateKey = privateKey; 164 this.publicKey = publicKey; 165 } 166 167 168 /** 169 * Returns the public EC key. 170 * 171 * @return The public EC key. 172 */ 173 public ECPublicKey getPublicKey() { 174 175 return publicKey; 176 } 177 178 179 /** 180 * Returns the private EC key. 181 * 182 * @return The private EC key. 183 */ 184 public ECPrivateKey getPrivateKey() { 185 186 return privateKey; 187 } 188 189 190 @Override 191 public Set<Curve> supportedEllipticCurves() { 192 193 return SUPPORTED_ELLIPTIC_CURVES; 194 } 195 196 197 /** 198 * Encrypts the specified clear text of a {@link JWEObject JWE object}. 199 * 200 * @param header The JSON Web Encryption (JWE) header. Must specify 201 * a supported JWE algorithm and method. Must not be 202 * {@code null}. 203 * @param clearText The clear text to encrypt. Must not be {@code null}. 204 * 205 * @return The resulting JWE crypto parts. 206 * 207 * @throws JOSEException If the JWE algorithm or method is not 208 * supported or if encryption failed for some 209 * other internal reason. 210 */ 211 @Deprecated 212 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 213 throws JOSEException { 214 215 return encrypt(header, clearText, AAD.compute(header)); 216 } 217 218 219 @Override 220 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText, final byte[] aad) 221 throws JOSEException { 222 223 // Generate ephemeral EC key pair on the same curve as the consumer's public key 224 KeyPair ephemeralKeyPair = generateEphemeralKeyPair(publicKey.getParams()); 225 ECPublicKey ephemeralPublicKey = (ECPublicKey)ephemeralKeyPair.getPublic(); 226 ECPrivateKey ephemeralPrivateKey = (ECPrivateKey)ephemeralKeyPair.getPrivate(); 227 228 // Add the ephemeral public EC key to the header 229 JWEHeader updatedHeader = new JWEHeader.Builder(header). 230 ephemeralPublicKey(new ECKey.Builder(getCurve(), ephemeralPublicKey).build()). 231 build(); 232 233 SecretKey Z = ECDH1PU.deriveSenderZ( 234 privateKey, 235 publicKey, 236 ephemeralPrivateKey, 237 getJCAContext().getKeyEncryptionProvider() 238 ); 239 240 // for JWEObject we need update the AAD as well 241 final byte[] updatedAAD = Arrays.equals(AAD.compute(header), aad) ? AAD.compute(updatedHeader) : aad; 242 243 return encryptWithZ(updatedHeader, Z, clearText, updatedAAD); 244 } 245 246 247 /** 248 * Generates a new ephemeral EC key pair with the specified curve. 249 * 250 * @param ecParameterSpec The EC key spec. Must not be {@code null}. 251 * 252 * @return The EC key pair. 253 * 254 * @throws JOSEException If the EC key pair couldn't be generated. 255 */ 256 private KeyPair generateEphemeralKeyPair(final ECParameterSpec ecParameterSpec) 257 throws JOSEException { 258 259 Provider keProvider = getJCAContext().getKeyEncryptionProvider(); 260 261 try { 262 KeyPairGenerator generator; 263 264 if (keProvider != null) { 265 generator = KeyPairGenerator.getInstance("EC", keProvider); 266 } else { 267 generator = KeyPairGenerator.getInstance("EC"); 268 } 269 270 generator.initialize(ecParameterSpec); 271 return generator.generateKeyPair(); 272 } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { 273 throw new JOSEException("Couldn't generate ephemeral EC key pair: " + e.getMessage(), e); 274 } 275 } 276}