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                     && (type != ExtensionType.EXT_SERVER_NAME)) {
 423                 fatalSE(Alerts.alert_unsupported_extension,
 424                     "Server sent an unsupported extension: " + type);
 425             }
 426         }
 427 
 428         // Create a new session, we need to do the full handshake
 429         session = new SSLSessionImpl(protocolVersion, cipherSuite,
 430                             mesg.sessionId, getHostSE(), getPortSE());
 431         if (debug != null && Debug.isOn("handshake")) {
 432             System.out.println("** " + cipherSuite);
 433         }
 434     }
 435 
 436     /*
 437      * Server's own key was either a signing-only key, or was too
 438      * large for export rules ... this message holds an ephemeral
 439      * RSA key to use for key exchange.
 440      */
 441     private void serverKeyExchange(RSA_ServerKeyExchange mesg)
 442             throws IOException, GeneralSecurityException {
 443         if (debug != null && Debug.isOn("handshake")) {
 444             mesg.print(System.out);
 445         }
 446         if (!mesg.verify(serverKey, clnt_random, svr_random)) {
 447             fatalSE(Alerts.alert_handshake_failure,
 448                 "server key exchange invalid");
 449             // NOTREACHED
 450         }
 451         ephemeralServerKey = mesg.getPublicKey();
 452     }
 453 
 454 
 455     /*
 456      * Diffie-Hellman key exchange.  We save the server public key and
 457      * our own D-H algorithm object so we can defer key calculations
 458      * until after we've sent the client key exchange message (which
 459      * gives client and server some useful parallelism).
 460      */
 461     private void serverKeyExchange(DH_ServerKeyExchange mesg)
 462             throws IOException {
 463         if (debug != null && Debug.isOn("handshake")) {
 464             mesg.print(System.out);
 465         }
 466         dh = new DHCrypt(mesg.getModulus(), mesg.getBase(), sslContext.getSecureRandom());
 467         serverDH = mesg.getServerPublicKey();
 468     }
 469 
 470     private void serverKeyExchange(ECDH_ServerKeyExchange mesg) throws IOException {
 471         if (debug != null && Debug.isOn("handshake")) {
 472             mesg.print(System.out);
 473         }
 474         ECPublicKey key = mesg.getPublicKey();
 475         ecdh = new ECDHCrypt(key.getParams(), sslContext.getSecureRandom());
 476         ephemeralServerKey = key;
 477     }
 478 
 479     /*
 480      * The server's "Hello Done" message is the client's sign that
 481      * it's time to do all the hard work.
 482      */
 483     private void serverHelloDone(ServerHelloDone mesg) throws IOException {
 484         if (debug != null && Debug.isOn("handshake")) {
 485             mesg.print(System.out);
 486         }
 487         /*
 488          * Always make sure the input has been digested before we
 489          * start emitting data, to ensure the hashes are correctly
 490          * computed for the Finished and CertificateVerify messages
 491          * which we send (here).
 492          */
 493         input.digestNow();
 494 
 495         /*
 496          * FIRST ... if requested, send an appropriate Certificate chain
 497          * to authenticate the client, and remember the associated private
 498          * key to sign the CertificateVerify message.
 499          */
 500         PrivateKey signingKey = null;
 501 
 502         if (certRequest != null) {
 503             X509ExtendedKeyManager km = sslContext.getX509KeyManager();
 504 
 505             ArrayList<String> keytypesTmp = new ArrayList<String>(4);
 506 
 507             for (int i = 0; i < certRequest.types.length; i++) {
 508                 String typeName;
 509 
 510                 switch (certRequest.types[i]) {
 511                 case CertificateRequest.cct_rsa_sign:
 512                     typeName = "RSA";
 513                     break;
 514 
 515                 case CertificateRequest.cct_dss_sign:
 516                     typeName = "DSA";
 517                     break;
 518 
 519                 case CertificateRequest.cct_ecdsa_sign:
 520                     // ignore if we do not have EC crypto available
 521                     typeName = JsseJce.isEcAvailable() ? "EC" : null;
 522                     break;
 523 
 524                 // Fixed DH/ECDH client authentication not supported
 525                 case CertificateRequest.cct_rsa_fixed_dh:
 526                 case CertificateRequest.cct_dss_fixed_dh:
 527                 case CertificateRequest.cct_rsa_fixed_ecdh:
 528                 case CertificateRequest.cct_ecdsa_fixed_ecdh:
 529                 // Any other values (currently not used in TLS)
 530                 case CertificateRequest.cct_rsa_ephemeral_dh:
 531                 case CertificateRequest.cct_dss_ephemeral_dh:
 532                 default:
 533                     typeName = null;
 534                     break;
 535                 }
 536 
 537                 if ((typeName != null) && (!keytypesTmp.contains(typeName))) {
 538                     keytypesTmp.add(typeName);
 539                 }
 540             }
 541 
 542             String alias = null;
 543             int keytypesTmpSize = keytypesTmp.size();
 544             if (keytypesTmpSize != 0) {
 545                 String keytypes[] =
 546                         keytypesTmp.toArray(new String[keytypesTmpSize]);
 547 
 548                 if (conn != null) {
 549                     alias = km.chooseClientAlias(keytypes,
 550                         certRequest.getAuthorities(), conn);
 551                 } else {
 552                     alias = km.chooseEngineClientAlias(keytypes,
 553                         certRequest.getAuthorities(), engine);
 554                 }
 555             }
 556 
 557             CertificateMsg m1 = null;
 558             if (alias != null) {
 559                 X509Certificate[] certs = km.getCertificateChain(alias);
 560                 if ((certs != null) && (certs.length != 0)) {
 561                     PublicKey publicKey = certs[0].getPublicKey();
 562                     // for EC, make sure we use a supported named curve
 563                     if (publicKey instanceof ECPublicKey) {
 564                         ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
 565                         int index = SupportedEllipticCurvesExtension.getCurveIndex(params);
 566                         if (!SupportedEllipticCurvesExtension.isSupported(index)) {
 567                             publicKey = null;
 568                         }
 569                     }
 570                     if (publicKey != null) {
 571                         m1 = new CertificateMsg(certs);
 572                         signingKey = km.getPrivateKey(alias);
 573                         session.setLocalPrivateKey(signingKey);
 574                         session.setLocalCertificates(certs);
 575                     }
 576                 }
 577             }
 578             if (m1 == null) {
 579                 //
 580                 // No appropriate cert was found ... report this to the
 581                 // server.  For SSLv3, send the no_certificate alert;
 582                 // TLS uses an empty cert chain instead.
 583                 //
 584                 if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
 585                     m1 = new CertificateMsg(new X509Certificate [0]);
 586                 } else {
 587                     warningSE(Alerts.alert_no_certificate);
 588                 }
 589             }
 590 
 591             //
 592             // At last ... send any client certificate chain.
 593             //
 594             if (m1 != null) {
 595                 if (debug != null && Debug.isOn("handshake")) {
 596                     m1.print(System.out);
 597                 }
 598                 m1.write(output);
 599             }
 600         }
 601 
 602         /*
 603          * SECOND ... send the client key exchange message.  The
 604          * procedure used is a function of the cipher suite selected;
 605          * one is always needed.
 606          */
 607         HandshakeMessage m2;
 608 
 609         switch (keyExchange) {
 610 
 611         case K_RSA:
 612         case K_RSA_EXPORT:
 613             if (serverKey == null) {
 614                 throw new SSLProtocolException
 615                         ("Server did not send certificate message");
 616             }
 617 
 618             if (!(serverKey instanceof RSAPublicKey)) {
 619                 throw new SSLProtocolException
 620                         ("Server certificate does not include an RSA key");
 621             }
 622 
 623             /*
 624              * For RSA key exchange, we randomly generate a new
 625              * pre-master secret and encrypt it with the server's
 626              * public key.  Then we save that pre-master secret
 627              * so that we can calculate the keying data later;
 628              * it's a performance speedup not to do that until
 629              * the client's waiting for the server response, but
 630              * more of a speedup for the D-H case.
 631              *
 632              * If the RSA_EXPORT scheme is active, when the public
 633              * key in the server certificate is less than or equal
 634              * to 512 bits in length, use the cert's public key,
 635              * otherwise, the ephemeral one.
 636              */
 637             PublicKey key;
 638             if (keyExchange == K_RSA) {
 639                 key = serverKey;
 640             } else {    // K_RSA_EXPORT
 641                 if (JsseJce.getRSAKeyLength(serverKey) <= 512) {
 642                     // extraneous ephemeralServerKey check done
 643                     // above in processMessage()
 644                     key = serverKey;
 645                 } else {
 646                     if (ephemeralServerKey == null) {
 647                         throw new SSLProtocolException("Server did not send" +
 648                             " a RSA_EXPORT Server Key Exchange message");
 649                     }
 650                     key = ephemeralServerKey;
 651                 }
 652             }
 653 
 654             m2 = new RSAClientKeyExchange(protocolVersion, maxProtocolVersion,
 655                                 sslContext.getSecureRandom(), key);
 656             break;
 657         case K_DH_RSA:
 658         case K_DH_DSS:
 659             /*
 660              * For DH Key exchange, we only need to make sure the server
 661              * knows our public key, so we calculate the same pre-master
 662              * secret.
 663              *
 664              * For certs that had DH keys in them, we send an empty
 665              * handshake message (no key) ... we flag this case by
 666              * passing a null "dhPublic" value.
 667              *
 668              * Otherwise we send ephemeral DH keys, unsigned.
 669              */
 670             // if (useDH_RSA || useDH_DSS)
 671             m2 = new DHClientKeyExchange();
 672             break;
 673         case K_DHE_RSA:
 674         case K_DHE_DSS:
 675         case K_DH_ANON:
 676             if (dh == null) {
 677                 throw new SSLProtocolException
 678                     ("Server did not send a DH Server Key Exchange message");
 679             }
 680             m2 = new DHClientKeyExchange(dh.getPublicKey());
 681             break;
 682         case K_ECDHE_RSA:
 683         case K_ECDHE_ECDSA:
 684         case K_ECDH_ANON:
 685             if (ecdh == null) {
 686                 throw new SSLProtocolException
 687                     ("Server did not send a ECDH Server Key Exchange message");
 688             }
 689             m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
 690             break;
 691         case K_ECDH_RSA:
 692         case K_ECDH_ECDSA:
 693             if (serverKey == null) {
 694                 throw new SSLProtocolException
 695                         ("Server did not send certificate message");
 696             }
 697             if (serverKey instanceof ECPublicKey == false) {
 698                 throw new SSLProtocolException
 699                         ("Server certificate does not include an EC key");
 700             }
 701             ECParameterSpec params = ((ECPublicKey)serverKey).getParams();
 702             ecdh = new ECDHCrypt(params, sslContext.getSecureRandom());
 703             m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
 704             break;
 705         case K_KRB5:
 706         case K_KRB5_EXPORT:
 707             String hostname = getHostSE();
 708             if (hostname == null) {
 709                 throw new IOException("Hostname is required" +
 710                                 " to use Kerberos cipher suites");
 711             }
 712             KerberosClientKeyExchange kerberosMsg = new KerberosClientKeyExchange
 713                 (hostname, isLoopbackSE(), getAccSE(), protocolVersion,
 714                 sslContext.getSecureRandom());
 715             // Record the principals involved in exchange
 716             session.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
 717             session.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
 718             m2 = kerberosMsg;
 719             break;
 720         default:
 721             // somethings very wrong
 722             throw new RuntimeException
 723                                 ("Unsupported key exchange: " + keyExchange);
 724         }
 725         if (debug != null && Debug.isOn("handshake")) {
 726             m2.print(System.out);
 727         }
 728         m2.write(output);
 729 
 730 
 731         /*
 732          * THIRD, send a "change_cipher_spec" record followed by the
 733          * "Finished" message.  We flush the messages we've queued up, to
 734          * get concurrency between client and server.  The concurrency is
 735          * useful as we calculate the master secret, which is needed both
 736          * to compute the "Finished" message, and to compute the keys used
 737          * to protect all records following the change_cipher_spec.
 738          */
 739 
 740         output.doHashes();
 741         output.flush();
 742 
 743         /*
 744          * We deferred calculating the master secret and this connection's
 745          * keying data; we do it now.  Deferring this calculation is good
 746          * from a performance point of view, since it lets us do it during
 747          * some time that network delays and the server's own calculations
 748          * would otherwise cause to be "dead" in the critical path.
 749          */
 750         SecretKey preMasterSecret;
 751         switch (keyExchange) {
 752         case K_RSA:
 753         case K_RSA_EXPORT:
 754             preMasterSecret = ((RSAClientKeyExchange)m2).preMaster;
 755             break;
 756         case K_KRB5:
 757         case K_KRB5_EXPORT:
 758             byte[] secretBytes =
 759                 ((KerberosClientKeyExchange)m2).getPreMasterSecret().getUnencrypted();
 760             preMasterSecret = new SecretKeySpec(secretBytes, "TlsPremasterSecret");
 761             break;
 762         case K_DHE_RSA:
 763         case K_DHE_DSS:
 764         case K_DH_ANON:
 765             preMasterSecret = dh.getAgreedSecret(serverDH);
 766             break;
 767         case K_ECDHE_RSA:
 768         case K_ECDHE_ECDSA:
 769         case K_ECDH_ANON:
 770             preMasterSecret = ecdh.getAgreedSecret(ephemeralServerKey);
 771             break;
 772         case K_ECDH_RSA:
 773         case K_ECDH_ECDSA:
 774             preMasterSecret = ecdh.getAgreedSecret(serverKey);
 775             break;
 776         default:
 777             throw new IOException("Internal error: unknown key exchange " + keyExchange);
 778         }
 779 
 780         calculateKeys(preMasterSecret, null);
 781 
 782         /*
 783          * FOURTH, if we sent a Certificate, we need to send a signed
 784          * CertificateVerify (unless the key in the client's certificate
 785          * was a Diffie-Hellman key).).
 786          *
 787          * This uses a hash of the previous handshake messages ... either
 788          * a nonfinal one (if the particular implementation supports it)
 789          * or else using the third element in the arrays of hashes being
 790          * computed.
 791          */
 792         if (signingKey != null) {
 793             CertificateVerify m3;
 794             try {
 795                 m3 = new CertificateVerify(protocolVersion, handshakeHash,
 796                     signingKey, session.getMasterSecret(),
 797                     sslContext.getSecureRandom());
 798             } catch (GeneralSecurityException e) {
 799                 fatalSE(Alerts.alert_handshake_failure,
 800                     "Error signing certificate verify", e);
 801                 // NOTREACHED, make compiler happy
 802                 m3 = null;
 803             }
 804             if (debug != null && Debug.isOn("handshake")) {
 805                 m3.print(System.out);
 806             }
 807             m3.write(output);
 808             output.doHashes();
 809         }
 810 
 811         /*
 812          * OK, that's that!
 813          */
 814         sendChangeCipherAndFinish(false);
 815     }
 816 
 817 
 818     /*
 819      * "Finished" is the last handshake message sent.  If we got this
 820      * far, the MAC has been validated post-decryption.  We validate
 821      * the two hashes here as an additional sanity check, protecting
 822      * the handshake against various active attacks.
 823      */
 824     private void serverFinished(Finished mesg) throws IOException {
 825         if (debug != null && Debug.isOn("handshake")) {
 826             mesg.print(System.out);
 827         }
 828 
 829         boolean verified = mesg.verify(protocolVersion, handshakeHash,
 830                                 Finished.SERVER, session.getMasterSecret());
 831 
 832         if (!verified) {
 833             fatalSE(Alerts.alert_illegal_parameter,
 834                        "server 'finished' message doesn't verify");
 835             // NOTREACHED
 836         }
 837 
 838         /*
 839          * OK, it verified.  If we're doing the fast handshake, add that
 840          * "Finished" message to the hash of handshake messages, then send
 841          * our own change_cipher_spec and Finished message for the server
 842          * to verify in turn.  These are the last handshake messages.
 843          *
 844          * In any case, update the session cache.  We're done handshaking,
 845          * so there are no threats any more associated with partially
 846          * completed handshakes.
 847          */
 848         if (resumingSession) {
 849             input.digestNow();
 850             sendChangeCipherAndFinish(true);
 851         }
 852         session.setLastAccessedTime(System.currentTimeMillis());
 853 
 854         if (!resumingSession) {
 855             if (session.isRejoinable()) {
 856                 ((SSLSessionContextImpl) sslContext
 857                         .engineGetClientSessionContext())
 858                         .put(session);
 859                 if (debug != null && Debug.isOn("session")) {
 860                     System.out.println("%% Cached client session: " + session);
 861                 }
 862             } else if (debug != null && Debug.isOn("session")) {
 863                 System.out.println(
 864                     "%% Didn't cache non-resumable client session: "
 865                     + session);
 866             }
 867         }
 868     }
 869 
 870 
 871     /*
 872      * Send my change-cipher-spec and Finished message ... done as the
 873      * last handshake act in either the short or long sequences.  In
 874      * the short one, we've already seen the server's Finished; in the
 875      * long one, we wait for it now.
 876      */
 877     private void sendChangeCipherAndFinish(boolean finishedTag)
 878             throws IOException {
 879         Finished mesg = new Finished(protocolVersion, handshakeHash,
 880                                 Finished.CLIENT, session.getMasterSecret());
 881 
 882         /*
 883          * Send the change_cipher_spec message, then the Finished message
 884          * which we just calculated (and protected using the keys we just
 885          * calculated).  Server responds with its Finished message, except
 886          * in the "fast handshake" (resume session) case.
 887          */
 888         sendChangeCipherSpec(mesg, finishedTag);
 889 
 890         /*
 891          * Update state machine so server MUST send 'finished' next.
 892          * (In "long" handshake case; in short case, we're responding
 893          * to its message.)
 894          */
 895         state = HandshakeMessage.ht_finished - 1;
 896     }
 897 
 898 
 899     /*
 900      * Returns a ClientHello message to kickstart renegotiations
 901      */
 902     HandshakeMessage getKickstartMessage() throws SSLException {
 903         ClientHello mesg = new ClientHello(sslContext.getSecureRandom(),
 904                                         protocolVersion);
 905         maxProtocolVersion = protocolVersion;
 906 
 907         clnt_random = mesg.clnt_random;
 908 
 909         //
 910         // Try to resume an existing session.  This might be mandatory,
 911         // given certain API options.
 912         //
 913         session = ((SSLSessionContextImpl)sslContext
 914                         .engineGetClientSessionContext())
 915                         .get(getHostSE(), getPortSE());
 916         if (debug != null && Debug.isOn("session")) {
 917             if (session != null) {
 918                 System.out.println("%% Client cached "
 919                     + session
 920                     + (session.isRejoinable() ? "" : " (not rejoinable)"));
 921             } else {
 922                 System.out.println("%% No cached client session");
 923             }
 924         }
 925         if ((session != null) && (session.isRejoinable() == false)) {
 926             session = null;
 927         }
 928 
 929         if (session != null) {
 930             CipherSuite sessionSuite = session.getSuite();
 931             ProtocolVersion sessionVersion = session.getProtocolVersion();
 932             if (isEnabled(sessionSuite) == false) {
 933                 if (debug != null && Debug.isOn("session")) {
 934                     System.out.println("%% can't resume, cipher disabled");
 935                 }
 936                 session = null;
 937             }
 938 
 939             if ((session != null) &&
 940                         (enabledProtocols.contains(sessionVersion) == false)) {
 941                 if (debug != null && Debug.isOn("session")) {
 942                     System.out.println("%% can't resume, protocol disabled");
 943                 }
 944                 session = null;
 945             }
 946 
 947             if (session != null) {
 948                 if (debug != null) {
 949                     if (Debug.isOn("handshake") || Debug.isOn("session")) {
 950                         System.out.println("%% Try resuming " + session
 951                             + " from port " + getLocalPortSE());
 952                     }
 953                 }
 954                 mesg.sessionId = session.getSessionId();
 955 
 956                 mesg.protocolVersion = sessionVersion;
 957                 maxProtocolVersion = sessionVersion;
 958 
 959                 // Update SSL version number in underlying SSL socket and
 960                 // handshake output stream, so that the output records (at the
 961                 // record layer) have the correct version
 962                 setVersion(sessionVersion);
 963             }
 964 
 965             //
 966             // don't say much beyond the obvious if we _must_ resume.
 967             //
 968             if (!enableNewSession) {
 969                 if (session == null) {
 970                     throw new SSLException(
 971                         "Can't reuse existing SSL client session");
 972                 }
 973                 mesg.setServerName(this.getHostSE());
 974                 mesg.setCipherSuites(new CipherSuiteList(sessionSuite));
 975                 return mesg;
 976             }
 977         }
 978         if (session == null) {
 979             if (enableNewSession) {
 980                 mesg.sessionId = SSLSessionImpl.nullSession.getSessionId();
 981             } else {
 982                 throw new SSLException("No existing session to resume.");
 983             }
 984         }
 985         
 986         // Put an RFC4366 TLS server name indication extension into ClientHello.
 987         mesg.setServerName(this.getHostSE());
 988 
 989         //
 990         // All we have left to do is fill out the cipher suites.
 991         // (If this changes, change the 'return' above!)
 992         //
 993         mesg.setCipherSuites(enabledCipherSuites);
 994 
 995         return mesg;
 996     }
 997 
 998     /*
 999      * Fault detected during handshake.
1000      */
1001     void handshakeAlert(byte description) throws SSLProtocolException {
1002         String message = Alerts.alertDescription(description);
1003 
1004         if (debug != null && Debug.isOn("handshake")) {
1005             System.out.println("SSL - handshake alert: " + message);
1006         }
1007         throw new SSLProtocolException("handshake alert:  " + message);
1008     }
1009 
1010     /*
1011      * Unless we are using an anonymous ciphersuite, the server always
1012      * sends a certificate message (for the CipherSuites we currently
1013      * support). The trust manager verifies the chain for us.
1014      */
1015     private void serverCertificate(CertificateMsg mesg) throws IOException {
1016         if (debug != null && Debug.isOn("handshake")) {
1017             mesg.print(System.out);
1018         }
1019         X509Certificate[] peerCerts = mesg.getCertificateChain();
1020         if (peerCerts.length == 0) {
1021             fatalSE(Alerts.alert_bad_certificate,
1022                 "empty certificate chain");
1023         }
1024         // ask the trust manager to verify the chain
1025         X509TrustManager tm = sslContext.getX509TrustManager();
1026         try {
1027             // find out the key exchange algorithm used
1028             // use "RSA" for non-ephemeral "RSA_EXPORT"
1029             String keyExchangeString;
1030             if (keyExchange == K_RSA_EXPORT && !serverKeyExchangeReceived) {
1031                 keyExchangeString = K_RSA.name;
1032             } else {
1033                 keyExchangeString = keyExchange.name;
1034             }
1035 
1036             String identificator = getHostnameVerificationSE();
1037             if (tm instanceof X509ExtendedTrustManager) {
1038                 ((X509ExtendedTrustManager)tm).checkServerTrusted(
1039                         (peerCerts != null ?
1040                             peerCerts.clone() :
1041                             null),
1042                         keyExchangeString,
1043                         getHostSE(),
1044                         identificator);
1045             } else {
1046                 if (identificator != null) {
1047                     throw new RuntimeException(
1048                         "trust manager does not support peer identification");
1049                 }
1050 
1051                 tm.checkServerTrusted(
1052                     (peerCerts != null ?
1053                         peerCerts.clone() :
1054                         peerCerts),
1055                     keyExchangeString);
1056             }
1057         } catch (CertificateException e) {
1058             // This will throw an exception, so include the original error.
1059             fatalSE(Alerts.alert_certificate_unknown, e);
1060         }
1061         session.setPeerCertificates(peerCerts);
1062     }
1063 }