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.impl; 019 020 021import java.util.Collections; 022import java.util.LinkedHashSet; 023import java.util.Set; 024import javax.crypto.SecretKey; 025 026import com.nimbusds.jose.*; 027import com.nimbusds.jose.jwk.Curve; 028import com.nimbusds.jose.util.Base64URL; 029 030 031/** 032 * The base abstract class for Elliptic Curve Diffie-Hellman One-Pass Unified 033 * Model encrypters and decrypters of {@link com.nimbusds.jose.JWEObject JWE 034 * objects}. 035 * 036 * <p>Supports the following key management algorithms: 037 * 038 * <ul> 039 * <li>{@link JWEAlgorithm#ECDH_1PU} 040 * <li>{@link JWEAlgorithm#ECDH_1PU_A128KW} 041 * <li>{@link JWEAlgorithm#ECDH_1PU_A192KW} 042 * <li>{@link JWEAlgorithm#ECDH_1PU_A256KW} 043 * </ul> 044 * 045 * <p>Supports the following elliptic curves: 046 * 047 * <ul> 048 * <li>{@link Curve#P_256} 049 * <li>{@link Curve#P_384} 050 * <li>{@link Curve#P_521} 051 * <li>{@link Curve#X25519} 052 * </ul> 053 * 054 * <p>Supports the following content encryption algorithms for Direct key 055 * agreement mode: 056 * 057 * <ul> 058 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 059 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 060 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 061 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 062 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 063 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 064 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 065 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 066 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 067 * </ul> 068 * 069 * <p>Supports the following content encryption algorithms for Key wrapping 070 * 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 * </ul> 077 * 078 * @author Alexander Martynov 079 * @author Egor Puzanov 080 * @version 2023-09-10 081 */ 082public abstract class ECDH1PUCryptoProvider extends BaseJWEProvider { 083 084 085 /** 086 * The supported JWE algorithms by the ECDH crypto provider class. 087 */ 088 public static final Set<JWEAlgorithm> SUPPORTED_ALGORITHMS; 089 090 091 /** 092 * The supported encryption methods by the ECDH crypto provider class. 093 */ 094 public static final Set<EncryptionMethod> SUPPORTED_ENCRYPTION_METHODS = ContentCryptoProvider.SUPPORTED_ENCRYPTION_METHODS; 095 096 097 static { 098 Set<JWEAlgorithm> algs = new LinkedHashSet<>(); 099 algs.add(JWEAlgorithm.ECDH_1PU); 100 algs.add(JWEAlgorithm.ECDH_1PU_A128KW); 101 algs.add(JWEAlgorithm.ECDH_1PU_A192KW); 102 algs.add(JWEAlgorithm.ECDH_1PU_A256KW); 103 SUPPORTED_ALGORITHMS = Collections.unmodifiableSet(algs); 104 } 105 106 107 /** 108 * The elliptic curve. 109 */ 110 private final Curve curve; 111 112 113 /** 114 * The Concatenation Key Derivation Function (KDF). 115 */ 116 private final ConcatKDF concatKDF; 117 118 119 /** 120 * Creates a new Elliptic Curve Diffie-Hellman One-Pass Unified Model 121 * encryption / decryption provider. 122 * 123 * @param curve The elliptic curve. Must be supported and not 124 * {@code null}. 125 * @param cek The content encryption key (CEK) to use. If specified 126 * its algorithm must be "AES" or "ChaCha20" and its length 127 * must match the expected for the JWE encryption method 128 * ("enc"). If {@code null} a CEK will be generated for 129 * each JWE. 130 * 131 * @throws JOSEException If the elliptic curve is not supported. 132 */ 133 protected ECDH1PUCryptoProvider(final Curve curve, final SecretKey cek) 134 throws JOSEException { 135 136 super(SUPPORTED_ALGORITHMS, ContentCryptoProvider.SUPPORTED_ENCRYPTION_METHODS, cek); 137 138 Curve definedCurve = curve != null ? curve : new Curve("unknown"); 139 140 if (!supportedEllipticCurves().contains(curve)) { 141 throw new JOSEException(AlgorithmSupportMessage.unsupportedEllipticCurve( 142 definedCurve, supportedEllipticCurves())); 143 } 144 145 this.curve = curve; 146 147 concatKDF = new ConcatKDF("SHA-256"); 148 } 149 150 151 /** 152 * Returns the Concatenation Key Derivation Function (KDF). 153 * 154 * @return The concat KDF. 155 */ 156 protected ConcatKDF getConcatKDF() { 157 158 return concatKDF; 159 } 160 161 162 /** 163 * Returns the names of the supported elliptic curves. These correspond 164 * to the {@code crv} JWK parameter. 165 * 166 * @return The supported elliptic curves. 167 */ 168 public abstract Set<Curve> supportedEllipticCurves(); 169 170 171 /** 172 * Returns the elliptic curve of the key (JWK designation). 173 * 174 * @return The elliptic curve. 175 */ 176 public Curve getCurve() { 177 178 return curve; 179 } 180 181 182 /** 183 * Encrypts the specified plaintext using the specified shared secret 184 * ("Z"). 185 */ 186 protected JWECryptoParts encryptWithZ(final JWEHeader header, 187 final SecretKey Z, 188 final byte[] clearText, 189 final byte[] aad) 190 throws JOSEException { 191 192 final JWEAlgorithm alg = JWEHeaderValidation.getAlgorithmAndEnsureNotNull(header); 193 final ECDH.AlgorithmMode algMode = ECDH1PU.resolveAlgorithmMode(alg); 194 final EncryptionMethod enc = header.getEncryptionMethod(); 195 196 final SecretKey cek; 197 final Base64URL encryptedKey; // The CEK encrypted (second JWE part) 198 199 if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) { 200 if (isCEKProvided()) { 201 throw new JOSEException("The provided CEK is not supported"); 202 } 203 // Derive shared key via concat KDF 204 getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat 205 cek = ECDH1PU.deriveSharedKey(header, Z, getConcatKDF()); 206 207 return ContentCryptoProvider.encrypt(header, clearText, aad, cek, null, getJCAContext()); 208 } 209 210 if (algMode.equals(ECDH.AlgorithmMode.KW)) { 211 212 // Key wrapping mode supports only AES_CBC_HMAC_SHA2 213 // See https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04#section-2.1 214 if (!EncryptionMethod.Family.AES_CBC_HMAC_SHA.contains(enc)) { 215 throw new JOSEException(AlgorithmSupportMessage.unsupportedEncryptionMethod( 216 header.getEncryptionMethod(), 217 EncryptionMethod.Family.AES_CBC_HMAC_SHA)); 218 } 219 220 cek = getCEK(enc); 221 222 JWECryptoParts encrypted = ContentCryptoProvider.encrypt(header, clearText, aad, cek, null, getJCAContext()); 223 224 SecretKey sharedKey = ECDH1PU.deriveSharedKey(header, Z, encrypted.getAuthenticationTag(), getConcatKDF()); 225 encryptedKey = Base64URL.encode(AESKW.wrapCEK(cek, sharedKey, getJCAContext().getKeyEncryptionProvider())); 226 227 return new JWECryptoParts( 228 header, 229 encryptedKey, 230 encrypted.getInitializationVector(), 231 encrypted.getCipherText(), 232 encrypted.getAuthenticationTag() 233 ); 234 } 235 236 throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode); 237 } 238 239 240 /** 241 * Decrypts the encrypted JWE parts using the specified shared secret ("Z"). 242 */ 243 protected byte[] decryptWithZ(final JWEHeader header, 244 final byte[] aad, 245 final SecretKey Z, 246 final Base64URL encryptedKey, 247 final Base64URL iv, 248 final Base64URL cipherText, 249 final Base64URL authTag) 250 throws JOSEException { 251 252 final JWEAlgorithm alg = JWEHeaderValidation.getAlgorithmAndEnsureNotNull(header); 253 final ECDH.AlgorithmMode algMode = ECDH1PU.resolveAlgorithmMode(alg); 254 255 // Derive shared key via concat KDF 256 getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat 257 258 final SecretKey cek; 259 260 if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) { 261 cek = ECDH1PU.deriveSharedKey(header, Z, getConcatKDF()); 262 } else if (algMode.equals(ECDH.AlgorithmMode.KW)) { 263 if (encryptedKey == null) { 264 throw new JOSEException("Missing JWE encrypted key"); 265 } 266 267 SecretKey sharedKey = ECDH1PU.deriveSharedKey(header, Z, authTag, getConcatKDF()); 268 cek = AESKW.unwrapCEK(sharedKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider()); 269 } else { 270 throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode); 271 } 272 273 return ContentCryptoProvider.decrypt(header, aad, null, iv, cipherText, authTag, cek, getJCAContext()); 274 } 275}