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