001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2019, 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 encrypters and 033 * decrypters of {@link com.nimbusds.jose.JWEObject JWE objects}. 034 * 035 * <p>Supports the following key management algorithms: 036 * 037 * <ul> 038 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES} 039 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW} 040 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW} 041 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW} 042 * </ul> 043 * 044 * <p>Supports the following elliptic curves: 045 * 046 * <ul> 047 * <li>{@link com.nimbusds.jose.jwk.Curve#P_256} 048 * <li>{@link com.nimbusds.jose.jwk.Curve#P_384} 049 * <li>{@link com.nimbusds.jose.jwk.Curve#P_521} 050 * <li>{@link com.nimbusds.jose.jwk.Curve#X25519} 051 * </ul> 052 * 053 * <p>Supports the following content encryption algorithms: 054 * 055 * <ul> 056 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 057 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 058 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 059 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 060 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 061 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 062 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 063 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 064 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 065 * </ul> 066 * 067 * @author Tim McLean 068 * @author Vladimir Dzhuvinov 069 * @author Fernando González Callejas 070 * @author Egor Puzanov 071 * @version 2023-09-10 072 */ 073public abstract class ECDHCryptoProvider extends BaseJWEProvider { 074 075 076 /** 077 * The supported JWE algorithms by the ECDH crypto provider class. 078 */ 079 public static final Set<JWEAlgorithm> SUPPORTED_ALGORITHMS; 080 081 082 /** 083 * The supported encryption methods by the ECDH crypto provider class. 084 */ 085 public static final Set<EncryptionMethod> SUPPORTED_ENCRYPTION_METHODS = ContentCryptoProvider.SUPPORTED_ENCRYPTION_METHODS; 086 087 088 static { 089 Set<JWEAlgorithm> algs = new LinkedHashSet<>(); 090 algs.add(JWEAlgorithm.ECDH_ES); 091 algs.add(JWEAlgorithm.ECDH_ES_A128KW); 092 algs.add(JWEAlgorithm.ECDH_ES_A192KW); 093 algs.add(JWEAlgorithm.ECDH_ES_A256KW); 094 SUPPORTED_ALGORITHMS = Collections.unmodifiableSet(algs); 095 } 096 097 098 /** 099 * The elliptic curve. 100 */ 101 private final Curve curve; 102 103 104 /** 105 * The Concatenation Key Derivation Function (KDF). 106 */ 107 private final ConcatKDF concatKDF; 108 109 110 /** 111 * Creates a new Elliptic Curve Diffie-Hellman encryption /decryption 112 * provider. 113 * 114 * @param curve The elliptic curve. Must be supported and not 115 * {@code null}. 116 * @param cek The content encryption key (CEK) to use. If specified 117 * its algorithm must be "AES" or "ChaCha20" and its length 118 * must match the expected for the JWE encryption method 119 * ("enc"). If {@code null} a CEK will be generated for 120 * each JWE. 121 * 122 * @throws JOSEException If the elliptic curve is not supported. 123 */ 124 protected ECDHCryptoProvider(final Curve curve, final SecretKey cek) 125 throws JOSEException { 126 127 super(SUPPORTED_ALGORITHMS, ContentCryptoProvider.SUPPORTED_ENCRYPTION_METHODS, cek); 128 129 Curve definedCurve = curve != null ? curve : new Curve("unknown"); 130 131 if (! supportedEllipticCurves().contains(curve)) { 132 throw new JOSEException(AlgorithmSupportMessage.unsupportedEllipticCurve( 133 definedCurve, supportedEllipticCurves())); 134 } 135 136 this.curve = curve; 137 138 concatKDF = new ConcatKDF("SHA-256"); 139 } 140 141 142 /** 143 * Returns the Concatenation Key Derivation Function (KDF). 144 * 145 * @return The concat KDF. 146 */ 147 protected ConcatKDF getConcatKDF() { 148 149 return concatKDF; 150 } 151 152 153 /** 154 * Returns the names of the supported elliptic curves. These correspond 155 * to the {@code crv} EC JWK parameter. 156 * 157 * @return The supported elliptic curves. 158 */ 159 public abstract Set<Curve> supportedEllipticCurves(); 160 161 162 /** 163 * Returns the elliptic curve of the key (JWK designation). 164 * 165 * @return The elliptic curve. 166 */ 167 public Curve getCurve() { 168 169 return curve; 170 } 171 172 173 /** 174 * Encrypts the specified plaintext using the specified shared secret 175 * ("Z"). 176 */ 177 protected JWECryptoParts encryptWithZ(final JWEHeader header, final SecretKey Z, final byte[] clearText, final byte[] aad) 178 throws JOSEException { 179 180 final JWEAlgorithm alg = JWEHeaderValidation.getAlgorithmAndEnsureNotNull(header); 181 final ECDH.AlgorithmMode algMode = ECDH.resolveAlgorithmMode(alg); 182 final EncryptionMethod enc = header.getEncryptionMethod(); 183 184 // Derive shared key via concat KDF 185 getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat 186 SecretKey sharedKey = ECDH.deriveSharedKey(header, Z, getConcatKDF()); 187 188 final SecretKey cek; 189 final Base64URL encryptedKey; // The CEK encrypted (second JWE part) 190 191 if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) { 192 if (isCEKProvided()) { 193 throw new JOSEException("The provided CEK is not supported"); 194 } 195 cek = sharedKey; 196 encryptedKey = null; 197 } else if (algMode.equals(ECDH.AlgorithmMode.KW)) { 198 cek = getCEK(enc); 199 encryptedKey = Base64URL.encode(AESKW.wrapCEK(cek, sharedKey, getJCAContext().getKeyEncryptionProvider())); 200 } else { 201 throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode); 202 } 203 204 return ContentCryptoProvider.encrypt(header, clearText, aad, cek, encryptedKey, getJCAContext()); 205 } 206 207 208 /** 209 * Decrypts the encrypted JWE parts using the specified shared secret 210 * ("Z"). 211 */ 212 protected byte[] decryptWithZ(final JWEHeader header, 213 final byte[] aad, 214 final SecretKey Z, 215 final Base64URL encryptedKey, 216 final Base64URL iv, 217 final Base64URL cipherText, 218 final Base64URL authTag) 219 throws JOSEException { 220 221 final JWEAlgorithm alg = JWEHeaderValidation.getAlgorithmAndEnsureNotNull(header); 222 final ECDH.AlgorithmMode algMode = ECDH.resolveAlgorithmMode(alg); 223 224 // Derive shared key via concat KDF 225 getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat 226 SecretKey sharedKey = ECDH.deriveSharedKey(header, Z, getConcatKDF()); 227 228 final SecretKey cek; 229 230 if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) { 231 cek = sharedKey; 232 } else if (algMode.equals(ECDH.AlgorithmMode.KW)) { 233 if (encryptedKey == null) { 234 throw new JOSEException("Missing JWE encrypted key"); 235 } 236 cek = AESKW.unwrapCEK(sharedKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider()); 237 } else { 238 throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode); 239 } 240 241 return ContentCryptoProvider.decrypt(header, aad, encryptedKey, iv, cipherText, authTag, cek, getJCAContext()); 242 } 243}