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 * @version 2019-01-24 071 */ 072public abstract class ECDHCryptoProvider extends BaseJWEProvider { 073 074 075 /** 076 * The supported JWE algorithms by the ECDH crypto provider class. 077 */ 078 public static final Set<JWEAlgorithm> SUPPORTED_ALGORITHMS; 079 080 081 /** 082 * The supported encryption methods by the ECDH crypto provider class. 083 */ 084 public static final Set<EncryptionMethod> SUPPORTED_ENCRYPTION_METHODS = ContentCryptoProvider.SUPPORTED_ENCRYPTION_METHODS; 085 086 087 static { 088 Set<JWEAlgorithm> algs = new LinkedHashSet<>(); 089 algs.add(JWEAlgorithm.ECDH_ES); 090 algs.add(JWEAlgorithm.ECDH_ES_A128KW); 091 algs.add(JWEAlgorithm.ECDH_ES_A192KW); 092 algs.add(JWEAlgorithm.ECDH_ES_A256KW); 093 SUPPORTED_ALGORITHMS = Collections.unmodifiableSet(algs); 094 } 095 096 097 /** 098 * The elliptic curve. 099 */ 100 private final Curve curve; 101 102 103 /** 104 * The Concatenation Key Derivation Function (KDF). 105 */ 106 private final ConcatKDF concatKDF; 107 108 109 /** 110 * Creates a new Elliptic Curve Diffie-Hellman encryption /decryption 111 * provider. 112 * 113 * @param curve The elliptic curve. Must be supported and not 114 * {@code null}. 115 * 116 * @throws JOSEException If the elliptic curve is not supported. 117 */ 118 protected ECDHCryptoProvider(final Curve curve) 119 throws JOSEException { 120 121 super(SUPPORTED_ALGORITHMS, ContentCryptoProvider.SUPPORTED_ENCRYPTION_METHODS); 122 123 Curve definedCurve = curve != null ? curve : new Curve("unknown"); 124 125 if (! supportedEllipticCurves().contains(curve)) { 126 throw new JOSEException(AlgorithmSupportMessage.unsupportedEllipticCurve( 127 definedCurve, supportedEllipticCurves())); 128 } 129 130 this.curve = curve; 131 132 concatKDF = new ConcatKDF("SHA-256"); 133 } 134 135 136 /** 137 * Returns the Concatenation Key Derivation Function (KDF). 138 * 139 * @return The concat KDF. 140 */ 141 protected ConcatKDF getConcatKDF() { 142 143 return concatKDF; 144 } 145 146 147 /** 148 * Returns the names of the supported elliptic curves. These correspond 149 * to the {@code crv} EC JWK parameter. 150 * 151 * @return The supported elliptic curves. 152 */ 153 public abstract Set<Curve> supportedEllipticCurves(); 154 155 156 /** 157 * Returns the elliptic curve of the key (JWK designation). 158 * 159 * @return The elliptic curve. 160 */ 161 public Curve getCurve() { 162 163 return curve; 164 } 165 166 /** 167 * Encrypts the specified plaintext using the specified shared secret 168 * ("Z"). 169 */ 170 protected JWECryptoParts encryptWithZ(final JWEHeader header, final SecretKey Z, final byte[] clearText) 171 throws JOSEException { 172 173 return this.encryptWithZ(header, Z, clearText, null); 174 } 175 176 /** 177 * Encrypts the specified plaintext using the specified shared secret 178 * ("Z") and, if provided, the content encryption key (CEK). 179 */ 180 protected JWECryptoParts encryptWithZ(final JWEHeader header, 181 final SecretKey Z, 182 final byte[] clearText, 183 final SecretKey contentEncryptionKey) 184 throws JOSEException { 185 186 final JWEAlgorithm alg = header.getAlgorithm(); 187 final ECDH.AlgorithmMode algMode = ECDH.resolveAlgorithmMode(alg); 188 final EncryptionMethod enc = header.getEncryptionMethod(); 189 190 // Derive shared key via concat KDF 191 getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat 192 SecretKey sharedKey = ECDH.deriveSharedKey(header, Z, getConcatKDF()); 193 194 final SecretKey cek; 195 final Base64URL encryptedKey; // The CEK encrypted (second JWE part) 196 197 if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) { 198 cek = sharedKey; 199 encryptedKey = null; 200 } else if (algMode.equals(ECDH.AlgorithmMode.KW)) { 201 if(contentEncryptionKey != null) { // Use externally supplied CEK 202 cek = contentEncryptionKey; 203 } else { // Generate the CEK according to the enc method 204 cek = ContentCryptoProvider.generateCEK(enc, getJCAContext().getSecureRandom()); 205 } 206 encryptedKey = Base64URL.encode(AESKW.wrapCEK(cek, sharedKey, getJCAContext().getKeyEncryptionProvider())); 207 } else { 208 throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode); 209 } 210 211 return ContentCryptoProvider.encrypt(header, clearText, cek, encryptedKey, getJCAContext()); 212 } 213 214 215 /** 216 * Decrypts the encrypted JWE parts using the specified shared secret 217 * ("Z"). 218 */ 219 protected byte[] decryptWithZ(final JWEHeader header, 220 final SecretKey Z, 221 final Base64URL encryptedKey, 222 final Base64URL iv, 223 final Base64URL cipherText, 224 final Base64URL authTag) 225 throws JOSEException { 226 227 final JWEAlgorithm alg = header.getAlgorithm(); 228 final ECDH.AlgorithmMode algMode = ECDH.resolveAlgorithmMode(alg); 229 230 // Derive shared key via concat KDF 231 getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat 232 SecretKey sharedKey = ECDH.deriveSharedKey(header, Z, getConcatKDF()); 233 234 final SecretKey cek; 235 236 if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) { 237 cek = sharedKey; 238 } else if (algMode.equals(ECDH.AlgorithmMode.KW)) { 239 if (encryptedKey == null) { 240 throw new JOSEException("Missing JWE encrypted key"); 241 } 242 cek = AESKW.unwrapCEK(sharedKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider()); 243 } else { 244 throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode); 245 } 246 247 return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext()); 248 } 249}