001package com.nimbusds.jose.crypto; 002 003 004import java.security.interfaces.ECPrivateKey; 005import java.security.interfaces.ECPublicKey; 006import java.util.Set; 007 008import javax.crypto.SecretKey; 009 010import com.nimbusds.jose.*; 011import com.nimbusds.jose.jwk.ECKey; 012import com.nimbusds.jose.util.Base64URL; 013 014 015/** 016 * Elliptic Curve Diffie-Hellman decrypter of 017 * {@link com.nimbusds.jose.JWEObject JWE objects}. This class is thread-safe. 018 * 019 * <p>Supports the following key management algorithms: 020 * 021 * <ul> 022 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES} 023 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW} 024 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW} 025 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW} 026 * </ul> 027 * 028 * <p>Supports the following elliptic curves: 029 * 030 * <ul> 031 * <li>{@link com.nimbusds.jose.jwk.ECKey.Curve#P_256} 032 * <li>{@link com.nimbusds.jose.jwk.ECKey.Curve#P_384} 033 * <li>{@link com.nimbusds.jose.jwk.ECKey.Curve#P_521} 034 * </ul> 035 * 036 * <p>Supports the following content encryption algorithms: 037 * 038 * <ul> 039 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 040 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 041 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 042 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 043 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 044 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 045 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 046 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 047 * </ul> 048 * 049 * @author Vladimir Dzhuvinov 050 * @version 2015-06-08 051 */ 052public class ECDHDecrypter extends ECDHCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware { 053 054 055 /** 056 * The private EC key. 057 */ 058 private final ECPrivateKey privateKey; 059 060 061 /** 062 * The critical header policy. 063 */ 064 private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral(); 065 066 067 /** 068 * Creates a new Elliptic Curve Diffie-Hellman decrypter. 069 * 070 * @param privateKey The private EC key. Must not be {@code null}. 071 * 072 * @throws JOSEException If the elliptic curve is not supported. 073 */ 074 public ECDHDecrypter(final ECPrivateKey privateKey) 075 throws JOSEException { 076 077 this(privateKey, null); 078 } 079 080 081 /** 082 * Creates a new Elliptic Curve Diffie-Hellman decrypter. 083 * 084 * @param ecJWK The EC JSON Web Key (JWK). Must contain a private 085 * part. Must not be {@code null}. 086 * 087 * @throws JOSEException If the elliptic curve is not supported. 088 */ 089 public ECDHDecrypter(final ECKey ecJWK) 090 throws JOSEException { 091 092 super(ecJWK.getCurve()); 093 094 if (! ecJWK.isPrivate()) { 095 throw new JOSEException("The EC JWK doesn't contain a private part"); 096 } 097 098 this.privateKey = ecJWK.toECPrivateKey(); 099 } 100 101 102 /** 103 * Creates a new Elliptic Curve Diffie-Hellman decrypter. 104 * 105 * @param privateKey The private EC key. Must not be {@code null}. 106 * @param defCritHeaders The names of the critical header parameters 107 * that are deferred to the application for 108 * processing, empty set or {@code null} if none. 109 * 110 * @throws JOSEException If the elliptic curve is not supported. 111 */ 112 public ECDHDecrypter(final ECPrivateKey privateKey, final Set<String> defCritHeaders) 113 throws JOSEException { 114 115 super(ECKey.Curve.forECParameterSpec(privateKey.getParams())); 116 117 critPolicy.setDeferredCriticalHeaderParams(defCritHeaders); 118 119 this.privateKey = privateKey; 120 } 121 122 123 /** 124 * Returns the private EC key. 125 * 126 * @return The private EC key. 127 */ 128 public ECPrivateKey getPrivateKey() { 129 130 return privateKey; 131 } 132 133 134 @Override 135 public Set<String> getProcessedCriticalHeaderParams() { 136 137 return critPolicy.getProcessedCriticalHeaderParams(); 138 } 139 140 141 @Override 142 public Set<String> getDeferredCriticalHeaderParams() { 143 144 return critPolicy.getProcessedCriticalHeaderParams(); 145 } 146 147 148 @Override 149 public byte[] decrypt(final JWEHeader header, 150 final Base64URL encryptedKey, 151 final Base64URL iv, 152 final Base64URL cipherText, 153 final Base64URL authTag) 154 throws JOSEException { 155 156 final JWEAlgorithm alg = header.getAlgorithm(); 157 final ECDH.AlgorithmMode algMode = ECDH.resolveAlgorithmMode(alg); 158 159 critPolicy.ensureHeaderPasses(header); 160 161 // Get ephemeral EC key 162 ECKey ephemeralKey = header.getEphemeralPublicKey(); 163 164 if (ephemeralKey == null) { 165 throw new JOSEException("Missing ephemeral public EC key \"epk\" JWE header parameter"); 166 } 167 168 ECPublicKey ephemeralPublicKey = ephemeralKey.toECPublicKey(); 169 170 // Derive 'Z' 171 SecretKey Z = ECDH.deriveSharedSecret( 172 ephemeralPublicKey, 173 privateKey, 174 getJCAContext().getKeyEncryptionProvider()); 175 176 // Derive shared key via concat KDF 177 getConcatKDF().getJCAContext().setProvider(getJCAContext().getMACProvider()); // update before concat 178 SecretKey sharedKey = ECDH.deriveSharedKey(header, Z, getConcatKDF()); 179 180 final SecretKey cek; 181 182 if (algMode.equals(ECDH.AlgorithmMode.DIRECT)) { 183 cek = sharedKey; 184 } else if (algMode.equals(ECDH.AlgorithmMode.KW)) { 185 if (encryptedKey == null) { 186 throw new JOSEException("Missing JWE encrypted key"); 187 } 188 cek = AESKW.unwrapCEK(sharedKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider()); 189 } else { 190 throw new JOSEException("Unexpected JWE ECDH algorithm mode: " + algMode); 191 } 192 193 return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext()); 194 } 195}