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