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