1 /*
   2  * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package sun.security.ssl;
  28 
  29 import java.math.BigInteger;
  30 import java.security.*;
  31 import javax.net.ssl.SSLHandshakeException;
  32 import javax.crypto.SecretKey;
  33 import javax.crypto.KeyAgreement;
  34 import javax.crypto.interfaces.DHPublicKey;
  35 import javax.crypto.spec.*;
  36 import java.util.EnumSet;
  37 
  38 import sun.security.util.KeyUtil;
  39 
  40 /**
  41  * This class implements the Diffie-Hellman key exchange algorithm.
  42  * D-H means combining your private key with your partners public key to
  43  * generate a number. The peer does the same with its private key and our
  44  * public key. Through the magic of Diffie-Hellman we both come up with the
  45  * same number. This number is secret (discounting MITM attacks) and hence
  46  * called the shared secret. It has the same length as the modulus, e.g. 512
  47  * or 1024 bit. Man-in-the-middle attacks are typically countered by an
  48  * independent authentication step using certificates (RSA, DSA, etc.).
  49  *
  50  * The thing to note is that the shared secret is constant for two partners
  51  * with constant private keys. This is often not what we want, which is why
  52  * it is generally a good idea to create a new private key for each session.
  53  * Generating a private key involves one modular exponentiation assuming
  54  * suitable D-H parameters are available.
  55  *
  56  * General usage of this class (TLS DHE case):
  57  *  . if we are server, call DHCrypt(keyLength,random). This generates
  58  *    an ephemeral keypair of the request length.
  59  *  . if we are client, call DHCrypt(modulus, base, random). This
  60  *    generates an ephemeral keypair using the parameters specified by
  61  *    the server.
  62  *  . send parameters and public value to remote peer
  63  *  . receive peers ephemeral public key
  64  *  . call getAgreedSecret() to calculate the shared secret
  65  *
  66  * In TLS the server chooses the parameter values itself, the client must use
  67  * those sent to it by the server.
  68  *
  69  * The use of ephemeral keys as described above also achieves what is called
  70  * "forward secrecy". This means that even if the authentication keys are
  71  * broken at a later date, the shared secret remains secure. The session is
  72  * compromised only if the authentication keys are already broken at the
  73  * time the key exchange takes place and an active MITM attack is used.
  74  * This is in contrast to straightforward encrypting RSA key exchanges.
  75  *
  76  * @author David Brownell
  77  */
  78 final class DHCrypt {
  79 
  80     // group parameters (prime modulus and generator)
  81     private BigInteger modulus;                 // P (aka N)
  82     private BigInteger base;                    // G (aka alpha)
  83 
  84     // our private key (including private component x)
  85     private PrivateKey privateKey;
  86 
  87     // public component of our key, X = (g ^ x) mod p
  88     private BigInteger publicValue;             // X (aka y)
  89 
  90     // the times to recove from failure if public key validation
  91     private static int MAX_FAILOVER_TIMES = 2;
  92 
  93     /**
  94      * Generate a Diffie-Hellman keypair of the specified size.
  95      */
  96     DHCrypt(int keyLength, SecureRandom random) {
  97         this(keyLength,
  98             PredefinedDHParameterSpecs.definedParams.get(keyLength), random);
  99     }
 100 
 101     /**
 102      * Generate a Diffie-Hellman keypair using the specified parameters.
 103      *
 104      * @param modulus the Diffie-Hellman modulus P
 105      * @param base the Diffie-Hellman base G
 106      */
 107     DHCrypt(BigInteger modulus, BigInteger base, SecureRandom random) {
 108         this(modulus.bitLength(),
 109                 new DHParameterSpec(modulus, base), random);
 110     }
 111 
 112     /**
 113      * Generate a Diffie-Hellman keypair using the named group.
 114      */
 115     DHCrypt(NamedGroup namedGroup, SecureRandom random) {
 116         this(-1,        // The length (-1) is not used in the implementation.
 117             SupportedGroupsExtension.getDHParameterSpec(namedGroup), random);
 118     }
 119 
 120     /**
 121      * Generate a Diffie-Hellman keypair using the specified size and
 122      * parameters.
 123      */
 124     private DHCrypt(int keyLength,
 125             DHParameterSpec params, SecureRandom random) {
 126 
 127         try {
 128             KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
 129             if (params != null) {
 130                 kpg.initialize(params, random);
 131             } else {
 132                 kpg.initialize(keyLength, random);
 133             }
 134 
 135             DHPublicKeySpec spec = generateDHPublicKeySpec(kpg);
 136             if (spec == null) {
 137                 throw new RuntimeException("Could not generate DH keypair");
 138             }
 139 
 140             publicValue = spec.getY();
 141             modulus = spec.getP();
 142             base = spec.getG();
 143         } catch (GeneralSecurityException e) {
 144             throw new RuntimeException("Could not generate DH keypair", e);
 145         }
 146     }
 147 
 148     static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {
 149         if (key instanceof DHPublicKey) {
 150             DHPublicKey dhKey = (DHPublicKey)key;
 151             DHParameterSpec params = dhKey.getParams();
 152             return new DHPublicKeySpec(dhKey.getY(),
 153                                     params.getP(), params.getG());
 154         }
 155         try {
 156             KeyFactory factory = JsseJce.getKeyFactory("DiffieHellman");
 157             return factory.getKeySpec(key, DHPublicKeySpec.class);
 158         } catch (Exception e) {
 159             throw new RuntimeException(e);
 160         }
 161     }
 162 
 163 
 164     /** Returns the Diffie-Hellman modulus. */
 165     BigInteger getModulus() {
 166         return modulus;
 167     }
 168 
 169     /** Returns the Diffie-Hellman base (generator).  */
 170     BigInteger getBase() {
 171         return base;
 172     }
 173 
 174     /**
 175      * Gets the public key of this end of the key exchange.
 176      */
 177     BigInteger getPublicKey() {
 178         return publicValue;
 179     }
 180 
 181     /**
 182      * Get the secret data that has been agreed on through Diffie-Hellman
 183      * key agreement protocol.  Note that in the two party protocol, if
 184      * the peer keys are already known, no other data needs to be sent in
 185      * order to agree on a secret.  That is, a secured message may be
 186      * sent without any mandatory round-trip overheads.
 187      *
 188      * <P>It is illegal to call this member function if the private key
 189      * has not been set (or generated).
 190      *
 191      * @param  peerPublicKey the peer's public key.
 192      * @param  keyIsValidated whether the {@code peerPublicKey} has beed
 193      *         validated
 194      * @return the secret, which is an unsigned big-endian integer
 195      *         the same size as the Diffie-Hellman modulus.
 196      */
 197     SecretKey getAgreedSecret(BigInteger peerPublicValue,
 198             boolean keyIsValidated) throws SSLHandshakeException {
 199         try {
 200             KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
 201             DHPublicKeySpec spec =
 202                         new DHPublicKeySpec(peerPublicValue, modulus, base);
 203             PublicKey publicKey = kf.generatePublic(spec);
 204             KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
 205 
 206             // validate the Diffie-Hellman public key
 207             if (!keyIsValidated &&
 208                     !KeyUtil.isOracleJCEProvider(ka.getProvider().getName())) {
 209                 try {
 210                     KeyUtil.validate(spec);
 211                 } catch (InvalidKeyException ike) {
 212                     // prefer handshake_failure alert to internal_error alert
 213                     throw new SSLHandshakeException(ike.getMessage());
 214                 }
 215             }
 216 
 217             ka.init(privateKey);
 218             ka.doPhase(publicKey, true);
 219             return ka.generateSecret("TlsPremasterSecret");
 220         } catch (GeneralSecurityException e) {
 221             throw (SSLHandshakeException) new SSLHandshakeException(
 222                 "Could not generate secret").initCause(e);
 223         }
 224     }
 225 
 226     // Check constraints of the specified DH public key.
 227     void checkConstraints(AlgorithmConstraints constraints,
 228             BigInteger peerPublicValue) throws SSLHandshakeException {
 229 
 230         try {
 231             KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
 232             DHPublicKeySpec spec =
 233                         new DHPublicKeySpec(peerPublicValue, modulus, base);
 234             DHPublicKey publicKey = (DHPublicKey)kf.generatePublic(spec);
 235 
 236             // check constraints of DHPublicKey
 237             if (!constraints.permits(
 238                     EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
 239                 throw new SSLHandshakeException(
 240                     "DHPublicKey does not comply to algorithm constraints");
 241             }
 242         } catch (GeneralSecurityException gse) {
 243             throw (SSLHandshakeException) new SSLHandshakeException(
 244                     "Could not generate DHPublicKey").initCause(gse);
 245         }
 246     }
 247 
 248     // Generate and validate DHPublicKeySpec
 249     private DHPublicKeySpec generateDHPublicKeySpec(KeyPairGenerator kpg)
 250             throws GeneralSecurityException {
 251 
 252         boolean doExtraValiadtion =
 253                     (!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
 254         for (int i = 0; i <= MAX_FAILOVER_TIMES; i++) {
 255             KeyPair kp = kpg.generateKeyPair();
 256             privateKey = kp.getPrivate();
 257             DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
 258 
 259             // validate the Diffie-Hellman public key
 260             if (doExtraValiadtion) {
 261                 try {
 262                     KeyUtil.validate(spec);
 263                 } catch (InvalidKeyException ivke) {
 264                     if (i == MAX_FAILOVER_TIMES) {
 265                         throw ivke;
 266                     }
 267                     // otherwise, ignore the exception and try the next one
 268                     continue;
 269                 }
 270             }
 271 
 272             return spec;
 273         }
 274 
 275         return null;
 276     }
 277 }
 278