src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java
changeset 50768 68fa3d4026ea
parent 47216 71c04702a3d5
child 53064 103ed9569fc8
equal deleted inserted replaced
50767:356eaea05bf0 50768:68fa3d4026ea
     1 /*
     1 /*
     2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    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
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 
       
    27 package sun.security.ssl;
    26 package sun.security.ssl;
    28 
    27 
    29 import java.io.IOException;
    28 import java.io.IOException;
    30 import java.io.PrintStream;
       
    31 import java.math.BigInteger;
    29 import java.math.BigInteger;
       
    30 import java.nio.ByteBuffer;
       
    31 import java.security.CryptoPrimitive;
       
    32 import java.security.GeneralSecurityException;
       
    33 import java.security.KeyFactory;
       
    34 import java.text.MessageFormat;
       
    35 import java.util.EnumSet;
       
    36 import java.util.Locale;
       
    37 import javax.crypto.SecretKey;
       
    38 import javax.crypto.interfaces.DHPublicKey;
       
    39 import javax.crypto.spec.DHParameterSpec;
       
    40 import javax.crypto.spec.DHPublicKeySpec;
    32 import javax.net.ssl.SSLHandshakeException;
    41 import javax.net.ssl.SSLHandshakeException;
    33 
    42 import sun.security.ssl.DHKeyExchange.DHECredentials;
    34 /*
    43 import sun.security.ssl.DHKeyExchange.DHEPossession;
    35  * Message used by clients to send their Diffie-Hellman public
    44 import sun.security.ssl.SSLHandshake.HandshakeMessage;
    36  * keys to servers.
    45 import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
    37  *
    46 import sun.security.util.HexDumpEncoder;
    38  * @author David Brownell
    47 
       
    48 /**
       
    49  * Pack of the "ClientKeyExchange" handshake message.
    39  */
    50  */
    40 final class DHClientKeyExchange extends HandshakeMessage {
    51 final class DHClientKeyExchange {
    41 
    52     static final DHClientKeyExchangeConsumer dhHandshakeConsumer =
    42     @Override
    53             new DHClientKeyExchangeConsumer();
    43     int messageType() {
    54     static final DHClientKeyExchangeProducer dhHandshakeProducer =
    44         return ht_client_key_exchange;
    55             new DHClientKeyExchangeProducer();
       
    56 
       
    57     /**
       
    58      * The DiffieHellman ClientKeyExchange handshake message.
       
    59      *
       
    60      * If the client has sent a certificate which contains a suitable
       
    61      * DiffieHellman key (for fixed_dh client authentication), then the
       
    62      * client public value is implicit and does not need to be sent again.
       
    63      * In this case, the client key exchange message will be sent, but it
       
    64      * MUST be empty.
       
    65      *
       
    66      * Currently, we don't support cipher suite that requires implicit public
       
    67      * key of client.
       
    68      */
       
    69     private static final
       
    70             class DHClientKeyExchangeMessage extends HandshakeMessage {
       
    71         private byte[] y;        // 1 to 2^16 - 1 bytes
       
    72 
       
    73         DHClientKeyExchangeMessage(
       
    74                 HandshakeContext handshakeContext) throws IOException {
       
    75             super(handshakeContext);
       
    76             // This happens in client side only.
       
    77             ClientHandshakeContext chc =
       
    78                     (ClientHandshakeContext)handshakeContext;
       
    79 
       
    80             DHEPossession dhePossession = null;
       
    81             for (SSLPossession possession : chc.handshakePossessions) {
       
    82                 if (possession instanceof DHEPossession) {
       
    83                     dhePossession = (DHEPossession)possession;
       
    84                     break;
       
    85                 }
       
    86             }
       
    87 
       
    88             if (dhePossession == null) {
       
    89                 // unlikely
       
    90                 chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
       
    91                     "No DHE credentials negotiated for client key exchange");
       
    92             }
       
    93 
       
    94             DHPublicKey publicKey = dhePossession.publicKey;
       
    95             DHParameterSpec params = publicKey.getParams();
       
    96             this.y = Utilities.toByteArray(publicKey.getY());
       
    97         }
       
    98 
       
    99         DHClientKeyExchangeMessage(HandshakeContext handshakeContext,
       
   100                 ByteBuffer m) throws IOException {
       
   101             super(handshakeContext);
       
   102             // This happens in server side only.
       
   103             ServerHandshakeContext shc =
       
   104                     (ServerHandshakeContext)handshakeContext;
       
   105 
       
   106             if (m.remaining() < 3) {
       
   107                 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
       
   108                     "Invalid DH ClientKeyExchange message: insufficient data");
       
   109             }
       
   110 
       
   111             this.y = Record.getBytes16(m);
       
   112 
       
   113             if (m.hasRemaining()) {
       
   114                 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
       
   115                     "Invalid DH ClientKeyExchange message: unknown extra data");
       
   116             }
       
   117         }
       
   118 
       
   119         @Override
       
   120         public SSLHandshake handshakeType() {
       
   121             return SSLHandshake.CLIENT_KEY_EXCHANGE;
       
   122         }
       
   123 
       
   124         @Override
       
   125         public int messageLength() {
       
   126             return y.length + 2;    // 2: length filed
       
   127         }
       
   128 
       
   129         @Override
       
   130         public void send(HandshakeOutStream hos) throws IOException {
       
   131             hos.putBytes16(y);
       
   132         }
       
   133 
       
   134         @Override
       
   135         public String toString() {
       
   136             MessageFormat messageFormat = new MessageFormat(
       
   137                 "\"DH ClientKeyExchange\": '{'\n" +
       
   138                 "  \"parameters\": '{'\n" +
       
   139                 "    \"dh_Yc\": '{'\n" +
       
   140                 "{0}\n" +
       
   141                 "    '}',\n" +
       
   142                 "  '}'\n" +
       
   143                 "'}'",
       
   144                 Locale.ENGLISH);
       
   145 
       
   146             HexDumpEncoder hexEncoder = new HexDumpEncoder();
       
   147             Object[] messageFields = {
       
   148                 Utilities.indent(
       
   149                         hexEncoder.encodeBuffer(y), "      "),
       
   150             };
       
   151             return messageFormat.format(messageFields);
       
   152         }
    45     }
   153     }
    46 
   154 
    47     /*
   155     /**
    48      * This value may be empty if it was included in the
   156      * The DiffieHellman "ClientKeyExchange" handshake message producer.
    49      * client's certificate ...
       
    50      */
   157      */
    51     private byte[] dh_Yc;               // 1 to 2^16 -1 bytes
   158     private static final
    52 
   159             class DHClientKeyExchangeProducer implements HandshakeProducer {
    53     BigInteger getClientPublicKey() {
   160         // Prevent instantiation of this class.
    54         return dh_Yc == null ? null : new BigInteger(1, dh_Yc);
   161         private DHClientKeyExchangeProducer() {
       
   162             // blank
       
   163         }
       
   164 
       
   165         @Override
       
   166         public byte[] produce(ConnectionContext context,
       
   167                 HandshakeMessage message) throws IOException {
       
   168             // The producing happens in client side only.
       
   169             ClientHandshakeContext chc = (ClientHandshakeContext)context;
       
   170 
       
   171             DHECredentials dheCredentials = null;
       
   172             for (SSLCredentials cd : chc.handshakeCredentials) {
       
   173                 if (cd instanceof DHECredentials) {
       
   174                     dheCredentials = (DHECredentials)cd;
       
   175                     break;
       
   176                 }
       
   177             }
       
   178 
       
   179             if (dheCredentials == null) {
       
   180                 chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
       
   181                     "No DHE credentials negotiated for client key exchange");
       
   182             }
       
   183 
       
   184 
       
   185             DHEPossession dhePossession = new DHEPossession(
       
   186                     dheCredentials, chc.sslContext.getSecureRandom());
       
   187             chc.handshakePossessions.add(dhePossession);
       
   188             DHClientKeyExchangeMessage ckem =
       
   189                     new DHClientKeyExchangeMessage(chc);
       
   190             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   191                 SSLLogger.fine(
       
   192                     "Produced DH ClientKeyExchange handshake message", ckem);
       
   193             }
       
   194 
       
   195             // Output the handshake message.
       
   196             ckem.write(chc.handshakeOutput);
       
   197             chc.handshakeOutput.flush();
       
   198 
       
   199             // update the states
       
   200             SSLKeyExchange ke = SSLKeyExchange.valueOf(
       
   201                     chc.negotiatedCipherSuite.keyExchange,
       
   202                     chc.negotiatedProtocol);
       
   203             if (ke == null) {
       
   204                 // unlikely
       
   205                 chc.conContext.fatal(Alert.INTERNAL_ERROR,
       
   206                         "Not supported key exchange type");
       
   207             } else {
       
   208                 SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
       
   209                 SecretKey masterSecret =
       
   210                         masterKD.deriveKey("MasterSecret", null);
       
   211                 chc.handshakeSession.setMasterSecret(masterSecret);
       
   212 
       
   213                 SSLTrafficKeyDerivation kd =
       
   214                         SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
       
   215                 if (kd == null) {
       
   216                     // unlikely
       
   217                     chc.conContext.fatal(Alert.INTERNAL_ERROR,
       
   218                             "Not supported key derivation: " +
       
   219                             chc.negotiatedProtocol);
       
   220                 } else {
       
   221                     chc.handshakeKeyDerivation =
       
   222                         kd.createKeyDerivation(chc, masterSecret);
       
   223                 }
       
   224             }
       
   225 
       
   226             // The handshake message has been delivered.
       
   227             return null;
       
   228         }
    55     }
   229     }
    56 
   230 
    57     /*
   231     /**
    58      * Either pass the client's public key explicitly (because it's
   232      * The DiffieHellman "ClientKeyExchange" handshake message consumer.
    59      * using DHE or DH_anon), or implicitly (the public key was in the
       
    60      * certificate).
       
    61      */
   233      */
    62     DHClientKeyExchange(BigInteger publicKey) {
   234     private static final
    63         dh_Yc = toByteArray(publicKey);
   235             class DHClientKeyExchangeConsumer implements SSLConsumer {
    64     }
   236         // Prevent instantiation of this class.
    65 
   237         private DHClientKeyExchangeConsumer() {
    66     DHClientKeyExchange() {
   238             // blank
    67         dh_Yc = null;
   239         }
    68     }
   240 
    69 
   241         @Override
    70     /*
   242         public void consume(ConnectionContext context,
    71      * Get the client's public key either explicitly or implicitly.
   243                 ByteBuffer message) throws IOException {
    72      * (It's ugly to have an empty record be sent in the latter case,
   244             // The consuming happens in server side only.
    73      * but that's what the protocol spec requires.)
   245             ServerHandshakeContext shc = (ServerHandshakeContext)context;
    74      */
   246 
    75     DHClientKeyExchange(HandshakeInStream input) throws IOException {
   247             DHEPossession dhePossession = null;
    76         if (input.available() >= 2) {
   248             for (SSLPossession possession : shc.handshakePossessions) {
    77             dh_Yc = input.getBytes16();
   249                 if (possession instanceof DHEPossession) {
    78         } else {
   250                     dhePossession = (DHEPossession)possession;
    79             // currently, we don't support cipher suites that requires
   251                     break;
    80             // implicit public key of client.
   252                 }
    81             throw new SSLHandshakeException(
   253             }
    82                     "Unsupported implicit client DiffieHellman public key");
   254 
    83         }
   255             if (dhePossession == null) {
    84     }
   256                 // unlikely
    85 
   257                 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
    86     @Override
   258                     "No expected DHE possessions for client key exchange");
    87     int messageLength() {
   259             }
    88         if (dh_Yc == null) {
   260 
    89             return 0;
   261             SSLKeyExchange ke = SSLKeyExchange.valueOf(
    90         } else {
   262                     shc.negotiatedCipherSuite.keyExchange,
    91             return dh_Yc.length + 2;
   263                     shc.negotiatedProtocol);
    92         }
   264             if (ke == null) {
    93     }
   265                 // unlikely
    94 
   266                 shc.conContext.fatal(Alert.INTERNAL_ERROR,
    95     @Override
   267                         "Not supported key exchange type");
    96     void send(HandshakeOutStream s) throws IOException {
   268             }
    97         if (dh_Yc != null && dh_Yc.length != 0) {
   269 
    98             s.putBytes16(dh_Yc);
   270             DHClientKeyExchangeMessage ckem =
    99         }
   271                     new DHClientKeyExchangeMessage(shc, message);
   100     }
   272             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   101 
   273                 SSLLogger.fine(
   102     @Override
   274                     "Consuming DH ClientKeyExchange handshake message", ckem);
   103     void print(PrintStream s) throws IOException {
   275             }
   104         s.println("*** ClientKeyExchange, DH");
   276 
   105 
   277             // create the credentials
   106         if (debug != null && Debug.isOn("verbose")) {
   278             try {
   107             Debug.println(s, "DH Public key", dh_Yc);
   279                 DHParameterSpec params = dhePossession.publicKey.getParams();
       
   280                 DHPublicKeySpec spec = new DHPublicKeySpec(
       
   281                         new BigInteger(1, ckem.y),
       
   282                         params.getP(), params.getG());
       
   283                 KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
       
   284                 DHPublicKey peerPublicKey =
       
   285                         (DHPublicKey)kf.generatePublic(spec);
       
   286 
       
   287                 // check constraints of peer DHPublicKey
       
   288                 if (!shc.algorithmConstraints.permits(
       
   289                         EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   290                         peerPublicKey)) {
       
   291                     throw new SSLHandshakeException(
       
   292                         "DHPublicKey does not comply to algorithm constraints");
       
   293                 }
       
   294 
       
   295                 NamedGroup namedGroup = NamedGroup.valueOf(params);
       
   296                 shc.handshakeCredentials.add(
       
   297                         new DHECredentials(peerPublicKey, namedGroup));
       
   298             } catch (GeneralSecurityException | java.io.IOException e) {
       
   299                 throw (SSLHandshakeException)(new SSLHandshakeException(
       
   300                         "Could not generate DHPublicKey").initCause(e));
       
   301             }
       
   302 
       
   303             // update the states
       
   304             SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
       
   305             SecretKey masterSecret =
       
   306                     masterKD.deriveKey("MasterSecret", null);
       
   307             shc.handshakeSession.setMasterSecret(masterSecret);
       
   308 
       
   309             SSLTrafficKeyDerivation kd =
       
   310                     SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
       
   311             if (kd == null) {
       
   312                 // unlikely
       
   313                 shc.conContext.fatal(Alert.INTERNAL_ERROR,
       
   314                     "Not supported key derivation: " + shc.negotiatedProtocol);
       
   315             } else {
       
   316                 shc.handshakeKeyDerivation =
       
   317                     kd.createKeyDerivation(shc, masterSecret);
       
   318             }
   108         }
   319         }
   109     }
   320     }
   110 }
   321 }