src/java.base/share/classes/sun/security/ssl/DHCrypt.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
parent 56541 92cbbfc996f3
child 56543 2352538d2f6e
equal deleted inserted replaced
56541:92cbbfc996f3 56542:56aaa6cb3693
     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