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