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