Home | History | Annotate | Download | only in ssl
      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 }
    248