1 /*
   2  * Copyright (c) 1996, 2012, 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 java.io.IOException;
  32 import javax.net.ssl.SSLHandshakeException;
  33 import javax.crypto.SecretKey;
  34 import javax.crypto.KeyAgreement;
  35 import javax.crypto.interfaces.DHPublicKey;
  36 import javax.crypto.spec.*;
  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         try {
  98             KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
  99             kpg.initialize(keyLength, random);
 100 
 101             DHPublicKeySpec spec = generateDHPublicKeySpec(kpg);
 102             if (spec == null) {
 103                 throw new RuntimeException("Could not generate DH keypair");
 104             }
 105 
 106             publicValue = spec.getY();
 107             modulus = spec.getP();
 108             base = spec.getG();
 109         } catch (GeneralSecurityException e) {
 110             throw new RuntimeException("Could not generate DH keypair", e);
 111         }
 112     }
 113 
 114 
 115     /**
 116      * Generate a Diffie-Hellman keypair using the specified parameters.
 117      *
 118      * @param modulus the Diffie-Hellman modulus P
 119      * @param base the Diffie-Hellman base G
 120      */
 121     DHCrypt(BigInteger modulus, BigInteger base, SecureRandom random) {
 122         this.modulus = modulus;
 123         this.base = base;
 124         try {
 125             KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
 126             DHParameterSpec params = new DHParameterSpec(modulus, base);
 127             kpg.initialize(params, random);
 128 
 129             DHPublicKeySpec spec = generateDHPublicKeySpec(kpg);
 130             if (spec == null) {
 131                 throw new RuntimeException("Could not generate DH keypair");
 132             }
 133 
 134             publicValue = spec.getY();
 135         } catch (GeneralSecurityException e) {
 136             throw new RuntimeException("Could not generate DH keypair", e);
 137         }
 138     }
 139 
 140 
 141     static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {
 142         if (key instanceof DHPublicKey) {
 143             DHPublicKey dhKey = (DHPublicKey)key;
 144             DHParameterSpec params = dhKey.getParams();
 145             return new DHPublicKeySpec(dhKey.getY(),
 146                                     params.getP(), params.getG());
 147         }
 148         try {
 149             KeyFactory factory = JsseJce.getKeyFactory("DH");
 150             return factory.getKeySpec(key, DHPublicKeySpec.class);
 151         } catch (Exception e) {
 152             throw new RuntimeException(e);
 153         }
 154     }
 155 
 156 
 157     /** Returns the Diffie-Hellman modulus. */
 158     BigInteger getModulus() {
 159         return modulus;
 160     }
 161 
 162     /** Returns the Diffie-Hellman base (generator).  */
 163     BigInteger getBase() {
 164         return base;
 165     }
 166 
 167     /**
 168      * Gets the public key of this end of the key exchange.
 169      */
 170     BigInteger getPublicKey() {
 171         return publicValue;
 172     }
 173 
 174     /**
 175      * Get the secret data that has been agreed on through Diffie-Hellman
 176      * key agreement protocol.  Note that in the two party protocol, if
 177      * the peer keys are already known, no other data needs to be sent in
 178      * order to agree on a secret.  That is, a secured message may be
 179      * sent without any mandatory round-trip overheads.
 180      *
 181      * <P>It is illegal to call this member function if the private key
 182      * has not been set (or generated).
 183      *
 184      * @param  peerPublicKey the peer's public key.
 185      * @param  keyIsValidated whether the {@code peerPublicKey} has beed
 186      *         validated
 187      * @return the secret, which is an unsigned big-endian integer
 188      *         the same size as the Diffie-Hellman modulus.
 189      */
 190     SecretKey getAgreedSecret(BigInteger peerPublicValue,
 191             boolean keyIsValidated) throws IOException {
 192         try {
 193             KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
 194             DHPublicKeySpec spec =
 195                         new DHPublicKeySpec(peerPublicValue, modulus, base);
 196             PublicKey publicKey = kf.generatePublic(spec);
 197             KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
 198 
 199             // validate the Diffie-Hellman public key
 200             if (!keyIsValidated &&
 201                     !KeyUtil.isOracleJCEProvider(ka.getProvider().getName())) {
 202                 try {
 203                     KeyUtil.validate(spec);
 204                 } catch (InvalidKeyException ike) {
 205                     // prefer handshake_failure alert to internal_error alert
 206                     throw new SSLHandshakeException(ike.getMessage());
 207                 }
 208             }
 209 
 210             ka.init(privateKey);
 211             ka.doPhase(publicKey, true);
 212             return ka.generateSecret("TlsPremasterSecret");
 213         } catch (GeneralSecurityException e) {
 214             throw new RuntimeException("Could not generate secret", e);
 215         }
 216     }
 217 
 218     // Generate and validate DHPublicKeySpec
 219     private DHPublicKeySpec generateDHPublicKeySpec(KeyPairGenerator kpg)
 220             throws GeneralSecurityException {
 221 
 222         boolean doExtraValiadtion =
 223                     (!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
 224         for (int i = 0; i <= MAX_FAILOVER_TIMES; i++) {
 225             KeyPair kp = kpg.generateKeyPair();
 226             privateKey = kp.getPrivate();
 227             DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
 228 
 229             // validate the Diffie-Hellman public key
 230             if (doExtraValiadtion) {
 231                 try {
 232                     KeyUtil.validate(spec);
 233                 } catch (InvalidKeyException ivke) {
 234                     if (i == MAX_FAILOVER_TIMES) {
 235                         throw ivke;
 236                     }
 237                     // otherwise, ignore the exception and try the next one
 238                     continue;
 239                 }
 240             }
 241 
 242             return spec;
 243         }
 244 
 245         return null;
 246     }
 247 }