1 /*
   2  * Copyright 1996-2008 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 
  27 package sun.security.ssl;
  28 
  29 import java.io.*;
  30 import java.math.BigInteger;
  31 import java.security.*;
  32 import java.util.*;
  33 
  34 import java.security.interfaces.ECPublicKey;
  35 import java.security.interfaces.RSAPublicKey;
  36 import java.security.spec.ECParameterSpec;
  37 
  38 import java.security.cert.X509Certificate;
  39 import java.security.cert.CertificateException;
  40 
  41 import javax.crypto.SecretKey;
  42 import javax.crypto.spec.SecretKeySpec;
  43 
  44 import javax.net.ssl.*;
  45 
  46 import javax.security.auth.Subject;
  47 import javax.security.auth.kerberos.KerberosPrincipal;
  48 import sun.security.jgss.krb5.Krb5Util;
  49 import sun.security.jgss.GSSUtil;
  50 
  51 import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager;
  52 
  53 import sun.security.ssl.HandshakeMessage.*;
  54 import sun.security.ssl.CipherSuite.*;
  55 import static sun.security.ssl.CipherSuite.*;
  56 import static sun.security.ssl.CipherSuite.KeyExchange.*;
  57 
  58 /**
  59  * ClientHandshaker does the protocol handshaking from the point
  60  * of view of a client.  It is driven asychronously by handshake messages
  61  * as delivered by the parent Handshaker class, and also uses
  62  * common functionality (e.g. key generation) that is provided there.
  63  *
  64  * @author David Brownell
  65  */
  66 final class ClientHandshaker extends Handshaker {
  67 
  68     // the server's public key from its certificate.
  69     private PublicKey serverKey;
  70 
  71     // the server's ephemeral public key from the server key exchange message
  72     // for ECDHE/ECDH_anon and RSA_EXPORT.
  73     private PublicKey ephemeralServerKey;
  74 
  75     // server's ephemeral public value for DHE/DH_anon key exchanges
  76     private BigInteger          serverDH;
  77 
  78     private DHCrypt             dh;
  79 
  80     private ECDHCrypt ecdh;
  81 
  82     private CertificateRequest  certRequest;
  83 
  84     private boolean serverKeyExchangeReceived;
  85 
  86     /*
  87      * The RSA PreMasterSecret needs to know the version of
  88      * ClientHello that was used on this handshake.  This represents
  89      * the "max version" this client is supporting.  In the
  90      * case of an initial handshake, it's the max version enabled,
  91      * but in the case of a resumption attempt, it's the version
  92      * of the session we're trying to resume.
  93      */
  94     private ProtocolVersion maxProtocolVersion;
  95 
  96     /*
  97      * Constructors
  98      */
  99     ClientHandshaker(SSLSocketImpl socket, SSLContextImpl context,
 100             ProtocolList enabledProtocols) {
 101         super(socket, context, enabledProtocols, true, true);
 102     }
 103 
 104     ClientHandshaker(SSLEngineImpl engine, SSLContextImpl context,
 105             ProtocolList enabledProtocols) {
 106         super(engine, context, enabledProtocols, true, true);
 107     }
 108 
 109     /*
 110      * This routine handles all the client side handshake messages, one at
 111      * a time.  Given the message type (and in some cases the pending cipher
 112      * spec) it parses the type-specific message.  Then it calls a function
 113      * that handles that specific message.
 114      *
 115      * It updates the state machine (need to verify it) as each message
 116      * is processed, and writes responses as needed using the connection
 117      * in the constructor.
 118      */
 119     void processMessage(byte type, int messageLen) throws IOException {
 120         if (state > type
 121                 && (type != HandshakeMessage.ht_hello_request
 122                     && state != HandshakeMessage.ht_client_hello)) {
 123             throw new SSLProtocolException(
 124                     "Handshake message sequence violation, " + type);
 125         }
 126 
 127         switch (type) {
 128         case HandshakeMessage.ht_hello_request:
 129             this.serverHelloRequest(new HelloRequest(input));
 130             break;
 131 
 132         case HandshakeMessage.ht_server_hello:
 133             this.serverHello(new ServerHello(input, messageLen));
 134             break;
 135 
 136         case HandshakeMessage.ht_certificate:
 137             if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON
 138                     || keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
 139                 fatalSE(Alerts.alert_unexpected_message,
 140                     "unexpected server cert chain");
 141                 // NOTREACHED
 142             }
 143             this.serverCertificate(new CertificateMsg(input));
 144             serverKey =
 145                 session.getPeerCertificates()[0].getPublicKey();
 146             break;
 147 
 148         case HandshakeMessage.ht_server_key_exchange:
 149             serverKeyExchangeReceived = true;
 150             switch (keyExchange) {
 151             case K_RSA_EXPORT:
 152                 /**
 153                  * The server key exchange message is sent by the server only
 154                  * when the server certificate message does not contain the
 155                  * proper amount of data to allow the client to exchange a
 156                  * premaster secret, such as when RSA_EXPORT is used and the
 157                  * public key in the server certificate is longer than 512 bits.
 158                  */
 159                 if (serverKey == null) {
 160                     throw new SSLProtocolException
 161                         ("Server did not send certificate message");
 162                 }
 163 
 164                 if (!(serverKey instanceof RSAPublicKey)) {
 165                     throw new SSLProtocolException("Protocol violation:" +
 166                         " the certificate type must be appropriate for the" +
 167                         " selected cipher suite's key exchange algorithm");
 168                 }
 169 
 170                 if (JsseJce.getRSAKeyLength(serverKey) <= 512) {
 171                     throw new SSLProtocolException("Protocol violation:" +
 172                         " server sent a server key exchange message for" +
 173                         " key exchange " + keyExchange +
 174                         " when the public key in the server certificate" +
 175                         " is less than or equal to 512 bits in length");
 176                 }
 177 
 178                 try {
 179                     this.serverKeyExchange(new RSA_ServerKeyExchange(input));
 180                 } catch (GeneralSecurityException e) {
 181                     throwSSLException("Server key", e);
 182                 }
 183                 break;
 184             case K_DH_ANON:
 185                 this.serverKeyExchange(new DH_ServerKeyExchange(input));
 186                 break;
 187             case K_DHE_DSS:
 188             case K_DHE_RSA:
 189                 try {
 190                     this.serverKeyExchange(new DH_ServerKeyExchange(
 191                         input, serverKey,
 192                         clnt_random.random_bytes, svr_random.random_bytes,
 193                         messageLen));
 194                 } catch (GeneralSecurityException e) {
 195                     throwSSLException("Server key", e);
 196                 }
 197                 break;
 198             case K_ECDHE_ECDSA:
 199             case K_ECDHE_RSA:
 200             case K_ECDH_ANON:
 201                 try {
 202                     this.serverKeyExchange(new ECDH_ServerKeyExchange
 203                         (input, serverKey, clnt_random.random_bytes,
 204                         svr_random.random_bytes));
 205                 } catch (GeneralSecurityException e) {
 206                     throwSSLException("Server key", e);
 207                 }
 208                 break;
 209             case K_RSA:
 210             case K_DH_RSA:
 211             case K_DH_DSS:
 212             case K_ECDH_ECDSA:
 213             case K_ECDH_RSA:
 214                 throw new SSLProtocolException("Protocol violation: server sent"
 215                     + " a server key exchange message for key exchange " + keyExchange);
 216             case K_KRB5:
 217             case K_KRB5_EXPORT:
 218                 throw new SSLProtocolException(
 219                     "unexpected receipt of server key exchange algorithm");
 220             default:
 221                 throw new SSLProtocolException(
 222                     "unsupported key exchange algorithm = "
 223                     + keyExchange);
 224             }
 225             break;
 226 
 227         case HandshakeMessage.ht_certificate_request:
 228             // save for later, it's handled by serverHelloDone
 229             if ((keyExchange == K_DH_ANON) || (keyExchange == K_ECDH_ANON)) {
 230                 throw new SSLHandshakeException(
 231                     "Client authentication requested for "+
 232                     "anonymous cipher suite.");
 233             } else if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
 234                 throw new SSLHandshakeException(
 235                     "Client certificate requested for "+
 236                     "kerberos cipher suite.");
 237             }
 238             certRequest = new CertificateRequest(input);
 239             if (debug != null && Debug.isOn("handshake")) {
 240                 certRequest.print(System.out);
 241             }
 242             break;
 243 
 244         case HandshakeMessage.ht_server_hello_done:
 245             this.serverHelloDone(new ServerHelloDone(input));
 246             break;
 247 
 248         case HandshakeMessage.ht_finished:
 249             this.serverFinished(new Finished(protocolVersion, input));
 250             break;
 251 
 252         default:
 253             throw new SSLProtocolException(
 254                 "Illegal client handshake msg, " + type);
 255         }
 256 
 257         //
 258         // Move state machine forward if the message handling
 259         // code didn't already do so
 260         //
 261         if (state < type) {
 262             state = type;
 263         }
 264     }
 265 
 266     /*
 267      * Used by the server to kickstart negotiations -- this requests a
 268      * "client hello" to renegotiate current cipher specs (e.g. maybe lots
 269      * of data has been encrypted with the same keys, or the server needs
 270      * the client to present a certificate).
 271      */
 272     private void serverHelloRequest(HelloRequest mesg) throws IOException {
 273         if (debug != null && Debug.isOn("handshake")) {
 274             mesg.print(System.out);
 275         }
 276 
 277         //
 278         // Could be (e.g. at connection setup) that we already
 279         // sent the "client hello" but the server's not seen it.
 280         //
 281         if (state < HandshakeMessage.ht_client_hello) {
 282             kickstart();
 283         }
 284     }
 285 
 286 
 287     /*
 288      * Server chooses session parameters given options created by the
 289      * client -- basically, cipher options, session id, and someday a
 290      * set of compression options.
 291      *
 292      * There are two branches of the state machine, decided by the
 293      * details of this message.  One is the "fast" handshake, where we
 294      * can resume the pre-existing session we asked resume.  The other
 295      * is a more expensive "full" handshake, with key exchange and
 296      * probably authentication getting done.
 297      */
 298     private void serverHello(ServerHello mesg) throws IOException {
 299         serverKeyExchangeReceived = false;
 300         if (debug != null && Debug.isOn("handshake")) {
 301             mesg.print(System.out);
 302         }
 303 
 304         // check if the server selected protocol version is OK for us
 305         ProtocolVersion mesgVersion = mesg.protocolVersion;
 306         if (enabledProtocols.contains(mesgVersion) == false) {
 307             throw new SSLHandshakeException
 308             ("Server chose unsupported or disabled protocol: " + mesgVersion);
 309         }
 310 
 311         // Set protocolVersion and propagate to SSLSocket and the
 312         // Handshake streams
 313         setVersion(mesgVersion);
 314 
 315         //
 316         // Save server nonce, we always use it to compute connection
 317         // keys and it's also used to create the master secret if we're
 318         // creating a new session (i.e. in the full handshake).
 319         //
 320         svr_random = mesg.svr_random;
 321 
 322         if (isEnabled(mesg.cipherSuite) == false) {
 323             fatalSE(Alerts.alert_illegal_parameter,
 324                 "Server selected disabled ciphersuite " + cipherSuite);
 325         }
 326         setCipherSuite(mesg.cipherSuite);
 327 
 328         if (mesg.compression_method != 0) {
 329             fatalSE(Alerts.alert_illegal_parameter,
 330                 "compression type not supported, "
 331                 + mesg.compression_method);
 332             // NOTREACHED
 333         }
 334 
 335         // so far so good, let's look at the session
 336         if (session != null) {
 337             // we tried to resume, let's see what the server decided
 338             if (session.getSessionId().equals(mesg.sessionId)) {
 339                 // server resumed the session, let's make sure everything
 340                 // checks out
 341 
 342                 // Verify that the session ciphers are unchanged.
 343                 CipherSuite sessionSuite = session.getSuite();
 344                 if (cipherSuite != sessionSuite) {
 345                     throw new SSLProtocolException
 346                         ("Server returned wrong cipher suite for session");
 347                 }
 348 
 349                 // verify protocol version match
 350                 ProtocolVersion sessionVersion = session.getProtocolVersion();
 351                 if (protocolVersion != sessionVersion) {
 352                     throw new SSLProtocolException
 353                         ("Server resumed session with wrong protocol version");
 354                 }
 355 
 356                 // validate subject identity
 357                 if (sessionSuite.keyExchange == K_KRB5 ||
 358                     sessionSuite.keyExchange == K_KRB5_EXPORT) {
 359                     Principal localPrincipal = session.getLocalPrincipal();
 360 
 361                     Subject subject = null;
 362                     try {
 363                         subject = AccessController.doPrivileged(
 364                             new PrivilegedExceptionAction<Subject>() {
 365                             public Subject run() throws Exception {
 366                                 return Krb5Util.getSubject(
 367                                     GSSUtil.CALLER_SSL_CLIENT,
 368                                     getAccSE());
 369                             }});
 370                     } catch (PrivilegedActionException e) {
 371                         subject = null;
 372                         if (debug != null && Debug.isOn("session")) {
 373                             System.out.println("Attempt to obtain" +
 374                                         " subject failed!");
 375                         }
 376                     }
 377 
 378                     if (subject != null) {
 379                         Set<KerberosPrincipal> principals =
 380                                 subject.getPrincipals(KerberosPrincipal.class);
 381                         if (!principals.contains(localPrincipal)) {
 382                             throw new SSLProtocolException("Server resumed" +
 383                                 " session with wrong subject identity");
 384                         } else {
 385                             if (debug != null && Debug.isOn("session"))
 386                                 System.out.println("Subject identity is same");
 387                         }
 388                     } else {
 389                         if (debug != null && Debug.isOn("session"))
 390                             System.out.println("Kerberos credentials are not" +
 391                                 " present in the current Subject; check if " +
 392                                 " javax.security.auth.useSubjectAsCreds" +
 393                                 " system property has been set to false");
 394                         throw new SSLProtocolException
 395                             ("Server resumed session with no subject");
 396                     }
 397                 }
 398 
 399                 // looks fine; resume it, and update the state machine.
 400                 resumingSession = true;
 401                 state = HandshakeMessage.ht_finished - 1;
 402                 calculateConnectionKeys(session.getMasterSecret());
 403                 if (debug != null && Debug.isOn("session")) {
 404                     System.out.println("%% Server resumed " + session);
 405                 }
 406                 return;
 407             } else {
 408                 // we wanted to resume, but the server refused
 409                 session = null;
 410                 if (!enableNewSession) {
 411                     throw new SSLException
 412                         ("New session creation is disabled");
 413                 }
 414             }
 415         }
 416 
 417         // check extensions
 418         for (HelloExtension ext : mesg.extensions.list()) {
 419             ExtensionType type = ext.type;
 420             if ((type != ExtensionType.EXT_ELLIPTIC_CURVES)
 421                     && (type != ExtensionType.EXT_EC_POINT_FORMATS)) {
 422                 fatalSE(Alerts.alert_unsupported_extension,
 423                     "Server sent an unsupported extension: " + type);
 424             }
 425         }
 426 
 427         // Create a new session, we need to do the full handshake
 428         session = new SSLSessionImpl(protocolVersion, cipherSuite,
 429                             mesg.sessionId, getHostSE(), getPortSE());
 430         if (debug != null && Debug.isOn("handshake")) {
 431             System.out.println("** " + cipherSuite);
 432         }
 433     }
 434 
 435     /*
 436      * Server's own key was either a signing-only key, or was too
 437      * large for export rules ... this message holds an ephemeral
 438      * RSA key to use for key exchange.
 439      */
 440     private void serverKeyExchange(RSA_ServerKeyExchange mesg)
 441             throws IOException, GeneralSecurityException {
 442         if (debug != null && Debug.isOn("handshake")) {
 443             mesg.print(System.out);
 444         }
 445         if (!mesg.verify(serverKey, clnt_random, svr_random)) {
 446             fatalSE(Alerts.alert_handshake_failure,
 447                 "server key exchange invalid");
 448             // NOTREACHED
 449         }
 450         ephemeralServerKey = mesg.getPublicKey();
 451     }
 452 
 453 
 454     /*
 455      * Diffie-Hellman key exchange.  We save the server public key and
 456      * our own D-H algorithm object so we can defer key calculations
 457      * until after we've sent the client key exchange message (which
 458      * gives client and server some useful parallelism).
 459      */
 460     private void serverKeyExchange(DH_ServerKeyExchange mesg)
 461             throws IOException {
 462         if (debug != null && Debug.isOn("handshake")) {
 463             mesg.print(System.out);
 464         }
 465         dh = new DHCrypt(mesg.getModulus(), mesg.getBase(), sslContext.getSecureRandom());
 466         serverDH = mesg.getServerPublicKey();
 467     }
 468 
 469     private void serverKeyExchange(ECDH_ServerKeyExchange mesg) throws IOException {
 470         if (debug != null && Debug.isOn("handshake")) {
 471             mesg.print(System.out);
 472         }
 473         ECPublicKey key = mesg.getPublicKey();
 474         ecdh = new ECDHCrypt(key.getParams(), sslContext.getSecureRandom());
 475         ephemeralServerKey = key;
 476     }
 477 
 478     /*
 479      * The server's "Hello Done" message is the client's sign that
 480      * it's time to do all the hard work.
 481      */
 482     private void serverHelloDone(ServerHelloDone mesg) throws IOException {
 483         if (debug != null && Debug.isOn("handshake")) {
 484             mesg.print(System.out);
 485         }
 486         /*
 487          * Always make sure the input has been digested before we
 488          * start emitting data, to ensure the hashes are correctly
 489          * computed for the Finished and CertificateVerify messages
 490          * which we send (here).
 491          */
 492         input.digestNow();
 493 
 494         /*
 495          * FIRST ... if requested, send an appropriate Certificate chain
 496          * to authenticate the client, and remember the associated private
 497          * key to sign the CertificateVerify message.
 498          */
 499         PrivateKey signingKey = null;
 500 
 501         if (certRequest != null) {
 502             X509ExtendedKeyManager km = sslContext.getX509KeyManager();
 503 
 504             ArrayList<String> keytypesTmp = new ArrayList<String>(4);
 505 
 506             for (int i = 0; i < certRequest.types.length; i++) {
 507                 String typeName;
 508 
 509                 switch (certRequest.types[i]) {
 510                 case CertificateRequest.cct_rsa_sign:
 511                     typeName = "RSA";
 512                     break;
 513 
 514                 case CertificateRequest.cct_dss_sign:
 515                     typeName = "DSA";
 516                     break;
 517 
 518                 case CertificateRequest.cct_ecdsa_sign:
 519                     // ignore if we do not have EC crypto available
 520                     typeName = JsseJce.isEcAvailable() ? "EC" : null;
 521                     break;
 522 
 523                 // Fixed DH/ECDH client authentication not supported
 524                 case CertificateRequest.cct_rsa_fixed_dh:
 525                 case CertificateRequest.cct_dss_fixed_dh:
 526                 case CertificateRequest.cct_rsa_fixed_ecdh:
 527                 case CertificateRequest.cct_ecdsa_fixed_ecdh:
 528                 // Any other values (currently not used in TLS)
 529                 case CertificateRequest.cct_rsa_ephemeral_dh:
 530                 case CertificateRequest.cct_dss_ephemeral_dh:
 531                 default:
 532                     typeName = null;
 533                     break;
 534                 }
 535 
 536                 if ((typeName != null) && (!keytypesTmp.contains(typeName))) {
 537                     keytypesTmp.add(typeName);
 538                 }
 539             }
 540 
 541             String alias = null;
 542             int keytypesTmpSize = keytypesTmp.size();
 543             if (keytypesTmpSize != 0) {
 544                 String keytypes[] =
 545                         keytypesTmp.toArray(new String[keytypesTmpSize]);
 546 
 547                 if (conn != null) {
 548                     alias = km.chooseClientAlias(keytypes,
 549                         certRequest.getAuthorities(), conn);
 550                 } else {
 551                     alias = km.chooseEngineClientAlias(keytypes,
 552                         certRequest.getAuthorities(), engine);
 553                 }
 554             }
 555 
 556             CertificateMsg m1 = null;
 557             if (alias != null) {
 558                 X509Certificate[] certs = km.getCertificateChain(alias);
 559                 if ((certs != null) && (certs.length != 0)) {
 560                     PublicKey publicKey = certs[0].getPublicKey();
 561                     // for EC, make sure we use a supported named curve
 562                     if (publicKey instanceof ECPublicKey) {
 563                         ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
 564                         int index = SupportedEllipticCurvesExtension.getCurveIndex(params);
 565                         if (!SupportedEllipticCurvesExtension.isSupported(index)) {
 566                             publicKey = null;
 567                         }
 568                     }
 569                     if (publicKey != null) {
 570                         m1 = new CertificateMsg(certs);
 571                         signingKey = km.getPrivateKey(alias);
 572                         session.setLocalPrivateKey(signingKey);
 573                         session.setLocalCertificates(certs);
 574                     }
 575                 }
 576             }
 577             if (m1 == null) {
 578                 //
 579                 // No appropriate cert was found ... report this to the
 580                 // server.  For SSLv3, send the no_certificate alert;
 581                 // TLS uses an empty cert chain instead.
 582                 //
 583                 if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
 584                     m1 = new CertificateMsg(new X509Certificate [0]);
 585                 } else {
 586                     warningSE(Alerts.alert_no_certificate);
 587                 }
 588             }
 589 
 590             //
 591             // At last ... send any client certificate chain.
 592             //
 593             if (m1 != null) {
 594                 if (debug != null && Debug.isOn("handshake")) {
 595                     m1.print(System.out);
 596                 }
 597                 m1.write(output);
 598             }
 599         }
 600 
 601         /*
 602          * SECOND ... send the client key exchange message.  The
 603          * procedure used is a function of the cipher suite selected;
 604          * one is always needed.
 605          */
 606         HandshakeMessage m2;
 607 
 608         switch (keyExchange) {
 609 
 610         case K_RSA:
 611         case K_RSA_EXPORT:
 612             if (serverKey == null) {
 613                 throw new SSLProtocolException
 614                         ("Server did not send certificate message");
 615             }
 616 
 617             if (!(serverKey instanceof RSAPublicKey)) {
 618                 throw new SSLProtocolException
 619                         ("Server certificate does not include an RSA key");
 620             }
 621 
 622             /*
 623              * For RSA key exchange, we randomly generate a new
 624              * pre-master secret and encrypt it with the server's
 625              * public key.  Then we save that pre-master secret
 626              * so that we can calculate the keying data later;
 627              * it's a performance speedup not to do that until
 628              * the client's waiting for the server response, but
 629              * more of a speedup for the D-H case.
 630              *
 631              * If the RSA_EXPORT scheme is active, when the public
 632              * key in the server certificate is less than or equal
 633              * to 512 bits in length, use the cert's public key,
 634              * otherwise, the ephemeral one.
 635              */
 636             PublicKey key;
 637             if (keyExchange == K_RSA) {
 638                 key = serverKey;
 639             } else {    // K_RSA_EXPORT
 640                 if (JsseJce.getRSAKeyLength(serverKey) <= 512) {
 641                     // extraneous ephemeralServerKey check done
 642                     // above in processMessage()
 643                     key = serverKey;
 644                 } else {
 645                     if (ephemeralServerKey == null) {
 646                         throw new SSLProtocolException("Server did not send" +
 647                             " a RSA_EXPORT Server Key Exchange message");
 648                     }
 649                     key = ephemeralServerKey;
 650                 }
 651             }
 652 
 653             m2 = new RSAClientKeyExchange(protocolVersion, maxProtocolVersion,
 654                                 sslContext.getSecureRandom(), key);
 655             break;
 656         case K_DH_RSA:
 657         case K_DH_DSS:
 658             /*
 659              * For DH Key exchange, we only need to make sure the server
 660              * knows our public key, so we calculate the same pre-master
 661              * secret.
 662              *
 663              * For certs that had DH keys in them, we send an empty
 664              * handshake message (no key) ... we flag this case by
 665              * passing a null "dhPublic" value.
 666              *
 667              * Otherwise we send ephemeral DH keys, unsigned.
 668              */
 669             // if (useDH_RSA || useDH_DSS)
 670             m2 = new DHClientKeyExchange();
 671             break;
 672         case K_DHE_RSA:
 673         case K_DHE_DSS:
 674         case K_DH_ANON:
 675             if (dh == null) {
 676                 throw new SSLProtocolException
 677                     ("Server did not send a DH Server Key Exchange message");
 678             }
 679             m2 = new DHClientKeyExchange(dh.getPublicKey());
 680             break;
 681         case K_ECDHE_RSA:
 682         case K_ECDHE_ECDSA:
 683         case K_ECDH_ANON:
 684             if (ecdh == null) {
 685                 throw new SSLProtocolException
 686                     ("Server did not send a ECDH Server Key Exchange message");
 687             }
 688             m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
 689             break;
 690         case K_ECDH_RSA:
 691         case K_ECDH_ECDSA:
 692             if (serverKey == null) {
 693                 throw new SSLProtocolException
 694                         ("Server did not send certificate message");
 695             }
 696             if (serverKey instanceof ECPublicKey == false) {
 697                 throw new SSLProtocolException
 698                         ("Server certificate does not include an EC key");
 699             }
 700             ECParameterSpec params = ((ECPublicKey)serverKey).getParams();
 701             ecdh = new ECDHCrypt(params, sslContext.getSecureRandom());
 702             m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
 703             break;
 704         case K_KRB5:
 705         case K_KRB5_EXPORT:
 706             String hostname = getHostSE();
 707             if (hostname == null) {
 708                 throw new IOException("Hostname is required" +
 709                                 " to use Kerberos cipher suites");
 710             }
 711             KerberosClientKeyExchange kerberosMsg = new KerberosClientKeyExchange
 712                 (hostname, isLoopbackSE(), getAccSE(), protocolVersion,
 713                 sslContext.getSecureRandom());
 714             // Record the principals involved in exchange
 715             session.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
 716             session.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
 717             m2 = kerberosMsg;
 718             break;
 719         default:
 720             // somethings very wrong
 721             throw new RuntimeException
 722                                 ("Unsupported key exchange: " + keyExchange);
 723         }
 724         if (debug != null && Debug.isOn("handshake")) {
 725             m2.print(System.out);
 726         }
 727         m2.write(output);
 728 
 729 
 730         /*
 731          * THIRD, send a "change_cipher_spec" record followed by the
 732          * "Finished" message.  We flush the messages we've queued up, to
 733          * get concurrency between client and server.  The concurrency is
 734          * useful as we calculate the master secret, which is needed both
 735          * to compute the "Finished" message, and to compute the keys used
 736          * to protect all records following the change_cipher_spec.
 737          */
 738 
 739         output.doHashes();
 740         output.flush();
 741 
 742         /*
 743          * We deferred calculating the master secret and this connection's
 744          * keying data; we do it now.  Deferring this calculation is good
 745          * from a performance point of view, since it lets us do it during
 746          * some time that network delays and the server's own calculations
 747          * would otherwise cause to be "dead" in the critical path.
 748          */
 749         SecretKey preMasterSecret;
 750         switch (keyExchange) {
 751         case K_RSA:
 752         case K_RSA_EXPORT:
 753             preMasterSecret = ((RSAClientKeyExchange)m2).preMaster;
 754             break;
 755         case K_KRB5:
 756         case K_KRB5_EXPORT:
 757             byte[] secretBytes =
 758                 ((KerberosClientKeyExchange)m2).getPreMasterSecret().getUnencrypted();
 759             preMasterSecret = new SecretKeySpec(secretBytes, "TlsPremasterSecret");
 760             break;
 761         case K_DHE_RSA:
 762         case K_DHE_DSS:
 763         case K_DH_ANON:
 764             preMasterSecret = dh.getAgreedSecret(serverDH);
 765             break;
 766         case K_ECDHE_RSA:
 767         case K_ECDHE_ECDSA:
 768         case K_ECDH_ANON:
 769             preMasterSecret = ecdh.getAgreedSecret(ephemeralServerKey);
 770             break;
 771         case K_ECDH_RSA:
 772         case K_ECDH_ECDSA:
 773             preMasterSecret = ecdh.getAgreedSecret(serverKey);
 774             break;
 775         default:
 776             throw new IOException("Internal error: unknown key exchange " + keyExchange);
 777         }
 778 
 779         calculateKeys(preMasterSecret, null);
 780 
 781         /*
 782          * FOURTH, if we sent a Certificate, we need to send a signed
 783          * CertificateVerify (unless the key in the client's certificate
 784          * was a Diffie-Hellman key).).
 785          *
 786          * This uses a hash of the previous handshake messages ... either
 787          * a nonfinal one (if the particular implementation supports it)
 788          * or else using the third element in the arrays of hashes being
 789          * computed.
 790          */
 791         if (signingKey != null) {
 792             CertificateVerify m3;
 793             try {
 794                 m3 = new CertificateVerify(protocolVersion, handshakeHash,
 795                     signingKey, session.getMasterSecret(),
 796                     sslContext.getSecureRandom());
 797             } catch (GeneralSecurityException e) {
 798                 fatalSE(Alerts.alert_handshake_failure,
 799                     "Error signing certificate verify", e);
 800                 // NOTREACHED, make compiler happy
 801                 m3 = null;
 802             }
 803             if (debug != null && Debug.isOn("handshake")) {
 804                 m3.print(System.out);
 805             }
 806             m3.write(output);
 807             output.doHashes();
 808         }
 809 
 810         /*
 811          * OK, that's that!
 812          */
 813         sendChangeCipherAndFinish(false);
 814     }
 815 
 816 
 817     /*
 818      * "Finished" is the last handshake message sent.  If we got this
 819      * far, the MAC has been validated post-decryption.  We validate
 820      * the two hashes here as an additional sanity check, protecting
 821      * the handshake against various active attacks.
 822      */
 823     private void serverFinished(Finished mesg) throws IOException {
 824         if (debug != null && Debug.isOn("handshake")) {
 825             mesg.print(System.out);
 826         }
 827 
 828         boolean verified = mesg.verify(protocolVersion, handshakeHash,
 829                                 Finished.SERVER, session.getMasterSecret());
 830 
 831         if (!verified) {
 832             fatalSE(Alerts.alert_illegal_parameter,
 833                        "server 'finished' message doesn't verify");
 834             // NOTREACHED
 835         }
 836 
 837         /*
 838          * OK, it verified.  If we're doing the fast handshake, add that
 839          * "Finished" message to the hash of handshake messages, then send
 840          * our own change_cipher_spec and Finished message for the server
 841          * to verify in turn.  These are the last handshake messages.
 842          *
 843          * In any case, update the session cache.  We're done handshaking,
 844          * so there are no threats any more associated with partially
 845          * completed handshakes.
 846          */
 847         if (resumingSession) {
 848             input.digestNow();
 849             sendChangeCipherAndFinish(true);
 850         }
 851         session.setLastAccessedTime(System.currentTimeMillis());
 852 
 853         if (!resumingSession) {
 854             if (session.isRejoinable()) {
 855                 ((SSLSessionContextImpl) sslContext
 856                         .engineGetClientSessionContext())
 857                         .put(session);
 858                 if (debug != null && Debug.isOn("session")) {
 859                     System.out.println("%% Cached client session: " + session);
 860                 }
 861             } else if (debug != null && Debug.isOn("session")) {
 862                 System.out.println(
 863                     "%% Didn't cache non-resumable client session: "
 864                     + session);
 865             }
 866         }
 867     }
 868 
 869 
 870     /*
 871      * Send my change-cipher-spec and Finished message ... done as the
 872      * last handshake act in either the short or long sequences.  In
 873      * the short one, we've already seen the server's Finished; in the
 874      * long one, we wait for it now.
 875      */
 876     private void sendChangeCipherAndFinish(boolean finishedTag)
 877             throws IOException {
 878         Finished mesg = new Finished(protocolVersion, handshakeHash,
 879                                 Finished.CLIENT, session.getMasterSecret());
 880 
 881         /*
 882          * Send the change_cipher_spec message, then the Finished message
 883          * which we just calculated (and protected using the keys we just
 884          * calculated).  Server responds with its Finished message, except
 885          * in the "fast handshake" (resume session) case.
 886          */
 887         sendChangeCipherSpec(mesg, finishedTag);
 888 
 889         /*
 890          * Update state machine so server MUST send 'finished' next.
 891          * (In "long" handshake case; in short case, we're responding
 892          * to its message.)
 893          */
 894         state = HandshakeMessage.ht_finished - 1;
 895     }
 896 
 897 
 898     /*
 899      * Returns a ClientHello message to kickstart renegotiations
 900      */
 901     HandshakeMessage getKickstartMessage() throws SSLException {
 902         ClientHello mesg = new ClientHello(sslContext.getSecureRandom(),
 903                                         protocolVersion);
 904         maxProtocolVersion = protocolVersion;
 905 
 906         clnt_random = mesg.clnt_random;
 907 
 908         //
 909         // Try to resume an existing session.  This might be mandatory,
 910         // given certain API options.
 911         //
 912         session = ((SSLSessionContextImpl)sslContext
 913                         .engineGetClientSessionContext())
 914                         .get(getHostSE(), getPortSE());
 915         if (debug != null && Debug.isOn("session")) {
 916             if (session != null) {
 917                 System.out.println("%% Client cached "
 918                     + session
 919                     + (session.isRejoinable() ? "" : " (not rejoinable)"));
 920             } else {
 921                 System.out.println("%% No cached client session");
 922             }
 923         }
 924         if ((session != null) && (session.isRejoinable() == false)) {
 925             session = null;
 926         }
 927 
 928         if (session != null) {
 929             CipherSuite sessionSuite = session.getSuite();
 930             ProtocolVersion sessionVersion = session.getProtocolVersion();
 931             if (isEnabled(sessionSuite) == false) {
 932                 if (debug != null && Debug.isOn("session")) {
 933                     System.out.println("%% can't resume, cipher disabled");
 934                 }
 935                 session = null;
 936             }
 937 
 938             if ((session != null) &&
 939                         (enabledProtocols.contains(sessionVersion) == false)) {
 940                 if (debug != null && Debug.isOn("session")) {
 941                     System.out.println("%% can't resume, protocol disabled");
 942                 }
 943                 session = null;
 944             }
 945 
 946             if (session != null) {
 947                 if (debug != null) {
 948                     if (Debug.isOn("handshake") || Debug.isOn("session")) {
 949                         System.out.println("%% Try resuming " + session
 950                             + " from port " + getLocalPortSE());
 951                     }
 952                 }
 953                 mesg.sessionId = session.getSessionId();
 954 
 955                 mesg.protocolVersion = sessionVersion;
 956                 maxProtocolVersion = sessionVersion;
 957 
 958                 // Update SSL version number in underlying SSL socket and
 959                 // handshake output stream, so that the output records (at the
 960                 // record layer) have the correct version
 961                 setVersion(sessionVersion);
 962             }
 963 
 964             //
 965             // don't say much beyond the obvious if we _must_ resume.
 966             //
 967             if (!enableNewSession) {
 968                 if (session == null) {
 969                     throw new SSLException(
 970                         "Can't reuse existing SSL client session");
 971                 }
 972                 mesg.setCipherSuites(new CipherSuiteList(sessionSuite));
 973                 return mesg;
 974             }
 975         }
 976         if (session == null) {
 977             if (enableNewSession) {
 978                 mesg.sessionId = SSLSessionImpl.nullSession.getSessionId();
 979             } else {
 980                 throw new SSLException("No existing session to resume.");
 981             }
 982         }
 983 
 984         //
 985         // All we have left to do is fill out the cipher suites.
 986         // (If this changes, change the 'return' above!)
 987         //
 988         mesg.setCipherSuites(enabledCipherSuites);
 989 
 990         return mesg;
 991     }
 992 
 993     /*
 994      * Fault detected during handshake.
 995      */
 996     void handshakeAlert(byte description) throws SSLProtocolException {
 997         String message = Alerts.alertDescription(description);
 998 
 999         if (debug != null && Debug.isOn("handshake")) {
1000             System.out.println("SSL - handshake alert: " + message);
1001         }
1002         throw new SSLProtocolException("handshake alert:  " + message);
1003     }
1004 
1005     /*
1006      * Unless we are using an anonymous ciphersuite, the server always
1007      * sends a certificate message (for the CipherSuites we currently
1008      * support). The trust manager verifies the chain for us.
1009      */
1010     private void serverCertificate(CertificateMsg mesg) throws IOException {
1011         if (debug != null && Debug.isOn("handshake")) {
1012             mesg.print(System.out);
1013         }
1014         X509Certificate[] peerCerts = mesg.getCertificateChain();
1015         if (peerCerts.length == 0) {
1016             fatalSE(Alerts.alert_bad_certificate,
1017                 "empty certificate chain");
1018         }
1019         // ask the trust manager to verify the chain
1020         X509TrustManager tm = sslContext.getX509TrustManager();
1021         try {
1022             // find out the key exchange algorithm used
1023             // use "RSA" for non-ephemeral "RSA_EXPORT"
1024             String keyExchangeString;
1025             if (keyExchange == K_RSA_EXPORT && !serverKeyExchangeReceived) {
1026                 keyExchangeString = K_RSA.name;
1027             } else {
1028                 keyExchangeString = keyExchange.name;
1029             }
1030 
1031             String identificator = getHostnameVerificationSE();
1032             if (tm instanceof X509ExtendedTrustManager) {
1033                 ((X509ExtendedTrustManager)tm).checkServerTrusted(
1034                         (peerCerts != null ?
1035                             peerCerts.clone() :
1036                             null),
1037                         keyExchangeString,
1038                         getHostSE(),
1039                         identificator);
1040             } else {
1041                 if (identificator != null) {
1042                     throw new RuntimeException(
1043                         "trust manager does not support peer identification");
1044                 }
1045 
1046                 tm.checkServerTrusted(
1047                     (peerCerts != null ?
1048                         peerCerts.clone() :
1049                         peerCerts),
1050                     keyExchangeString);
1051             }
1052         } catch (CertificateException e) {
1053             // This will throw an exception, so include the original error.
1054             fatalSE(Alerts.alert_certificate_unknown, e);
1055         }
1056         session.setPeerCertificates(peerCerts);
1057     }
1058 }