Principal localPrincipal = session.getLocalPrincipal(); Subject subject = null; try { subject = AccessController.doPrivileged( new PrivilegedExceptionAction<Subject>() { public Subject run() throws Exception { return Krb5Helper.getClientSubject(getAccSE()); }}); } catch (PrivilegedActionException e) { subject = null; if (debug != null && Debug.isOn("session")) { System.out.println("Attempt to obtain" + " subject failed!"); } } if (subject != null) { // Eliminate dependency on KerberosPrincipal Set<Principal> principals = subject.getPrincipals(Principal.class); if (!principals.contains(localPrincipal)) { throw new SSLProtocolException("Server resumed" + " session with wrong subject identity"); } else { if (debug != null && Debug.isOn("session")) System.out.println("Subject identity is same"); } } else { if (debug != null && Debug.isOn("session")) System.out.println("Kerberos credentials are not" + " present in the current Subject; check if " + " javax.security.auth.useSubjectAsCreds" + " system property has been set to false"); throw new SSLProtocolException ("Server resumed session with no subject"); } } // looks fine; resume it, and update the state machine. resumingSession = true; state = HandshakeMessage.ht_finished - 1; calculateConnectionKeys(session.getMasterSecret()); if (debug != null && Debug.isOn("session")) { System.out.println("%% Server resumed " + session); } } else { // we wanted to resume, but the server refused session = null; if (!enableNewSession) { throw new SSLException ("New session creation is disabled"); } } } if (resumingSession && session != null) { if (protocolVersion.v >= ProtocolVersion.TLS12.v) { handshakeHash.setCertificateVerifyAlg(null); } setHandshakeSessionSE(session); return; } // check extensions for (HelloExtension ext : mesg.extensions.list()) { ExtensionType type = ext.type; if ((type != ExtensionType.EXT_ELLIPTIC_CURVES) && (type != ExtensionType.EXT_EC_POINT_FORMATS) && (type != ExtensionType.EXT_SERVER_NAME) && (type != ExtensionType.EXT_RENEGOTIATION_INFO)) { fatalSE(Alerts.alert_unsupported_extension, "Server sent an unsupported extension: " + type); } } // Create a new session, we need to do the full handshake session = new SSLSessionImpl(protocolVersion, cipherSuite, getLocalSupportedSignAlgs(), mesg.sessionId, getHostSE(), getPortSE()); setHandshakeSessionSE(session); if (debug != null && Debug.isOn("handshake")) { System.out.println("** " + cipherSuite); } } /* * Server's own key was either a signing-only key, or was too * large for export rules ... this message holds an ephemeral * RSA key to use for key exchange. */ private void serverKeyExchange(RSA_ServerKeyExchange mesg) throws IOException, GeneralSecurityException { if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } if (!mesg.verify(serverKey, clnt_random, svr_random)) { fatalSE(Alerts.alert_handshake_failure, "server key exchange invalid"); // NOTREACHED } ephemeralServerKey = mesg.getPublicKey(); } /* * Diffie-Hellman key exchange. We save the server public key and * our own D-H algorithm object so we can defer key calculations * until after we've sent the client key exchange message (which * gives client and server some useful parallelism). */ private void serverKeyExchange(DH_ServerKeyExchange mesg) throws IOException { if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } dh = new DHCrypt(mesg.getModulus(), mesg.getBase(), sslContext.getSecureRandom()); serverDH = mesg.getServerPublicKey(); } private void serverKeyExchange(ECDH_ServerKeyExchange mesg) throws IOException { if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } ECPublicKey key = mesg.getPublicKey(); ecdh = new ECDHCrypt(key.getParams(), sslContext.getSecureRandom()); ephemeralServerKey = key; } /* * The server's "Hello Done" message is the client's sign that * it's time to do all the hard work. */ private void serverHelloDone(ServerHelloDone mesg) throws IOException { if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } /* * Always make sure the input has been digested before we * start emitting data, to ensure the hashes are correctly * computed for the Finished and CertificateVerify messages * which we send (here). */ input.digestNow(); /* * FIRST ... if requested, send an appropriate Certificate chain * to authenticate the client, and remember the associated private * key to sign the CertificateVerify message. */ PrivateKey signingKey = null; if (certRequest != null) { X509ExtendedKeyManager km = sslContext.getX509KeyManager(); ArrayList<String> keytypesTmp = new ArrayList<>(4); for (int i = 0; i < certRequest.types.length; i++) { String typeName; switch (certRequest.types[i]) { case CertificateRequest.cct_rsa_sign: typeName = "RSA"; break; case CertificateRequest.cct_dss_sign: typeName = "DSA"; break; case CertificateRequest.cct_ecdsa_sign: // ignore if we do not have EC crypto available typeName = JsseJce.isEcAvailable() ? "EC" : null; break; // Fixed DH/ECDH client authentication not supported case CertificateRequest.cct_rsa_fixed_dh: case CertificateRequest.cct_dss_fixed_dh: case CertificateRequest.cct_rsa_fixed_ecdh: case CertificateRequest.cct_ecdsa_fixed_ecdh: // Any other values (currently not used in TLS) case CertificateRequest.cct_rsa_ephemeral_dh: case CertificateRequest.cct_dss_ephemeral_dh: default: typeName = null; break; } if ((typeName != null) && (!keytypesTmp.contains(typeName))) { keytypesTmp.add(typeName); } } String alias = null; int keytypesTmpSize = keytypesTmp.size(); if (keytypesTmpSize != 0) { String keytypes[] = keytypesTmp.toArray(new String[keytypesTmpSize]); if (conn != null) { alias = km.chooseClientAlias(keytypes, certRequest.getAuthorities(), conn); } else { alias = km.chooseEngineClientAlias(keytypes, certRequest.getAuthorities(), engine); } } CertificateMsg m1 = null; if (alias != null) { X509Certificate[] certs = km.getCertificateChain(alias); if ((certs != null) && (certs.length != 0)) { PublicKey publicKey = certs[0].getPublicKey(); // for EC, make sure we use a supported named curve if (publicKey instanceof ECPublicKey) { ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); int index = SupportedEllipticCurvesExtension.getCurveIndex( params); if (!SupportedEllipticCurvesExtension.isSupported( index)) { publicKey = null; } } if (publicKey != null) { m1 = new CertificateMsg(certs); signingKey = km.getPrivateKey(alias); session.setLocalPrivateKey(signingKey); session.setLocalCertificates(certs); } } } if (m1 == null) { // // No appropriate cert was found ... report this to the // server. For SSLv3, send the no_certificate alert; // TLS uses an empty cert chain instead. // if (protocolVersion.v >= ProtocolVersion.TLS10.v) { m1 = new CertificateMsg(new X509Certificate [0]); } else { warningSE(Alerts.alert_no_certificate); } } // // At last ... send any client certificate chain. // if (m1 != null) { if (debug != null && Debug.isOn("handshake")) { m1.print(System.out); } m1.write(output); } } /* * SECOND ... send the client key exchange message. The * procedure used is a function of the cipher suite selected; * one is always needed. */ HandshakeMessage m2; switch (keyExchange) { case K_RSA: case K_RSA_EXPORT: if (serverKey == null) { throw new SSLProtocolException ("Server did not send certificate message"); } if (!(serverKey instanceof RSAPublicKey)) { throw new SSLProtocolException ("Server certificate does not include an RSA key"); } /* * For RSA key exchange, we randomly generate a new * pre-master secret and encrypt it with the server's * public key. Then we save that pre-master secret * so that we can calculate the keying data later; * it's a performance speedup not to do that until * the client's waiting for the server response, but * more of a speedup for the D-H case. * * If the RSA_EXPORT scheme is active, when the public * key in the server certificate is less than or equal * to 512 bits in length, use the cert's public key, * otherwise, the ephemeral one. */ PublicKey key; if (keyExchange == K_RSA) { key = serverKey; } else { // K_RSA_EXPORT if (JsseJce.getRSAKeyLength(serverKey) <= 512) { // extraneous ephemeralServerKey check done // above in processMessage() key = serverKey; } else { if (ephemeralServerKey == null) { throw new SSLProtocolException("Server did not send" + " a RSA_EXPORT Server Key Exchange message"); } key = ephemeralServerKey; } } m2 = new RSAClientKeyExchange(protocolVersion, maxProtocolVersion, sslContext.getSecureRandom(), key); break; case K_DH_RSA: case K_DH_DSS: /* * For DH Key exchange, we only need to make sure the server * knows our public key, so we calculate the same pre-master * secret. * * For certs that had DH keys in them, we send an empty * handshake message (no key) ... we flag this case by * passing a null "dhPublic" value. * * Otherwise we send ephemeral DH keys, unsigned. */ // if (useDH_RSA || useDH_DSS) m2 = new DHClientKeyExchange(); break; case K_DHE_RSA: case K_DHE_DSS: case K_DH_ANON: if (dh == null) { throw new SSLProtocolException ("Server did not send a DH Server Key Exchange message"); } m2 = new DHClientKeyExchange(dh.getPublicKey()); break; case K_ECDHE_RSA: case K_ECDHE_ECDSA: case K_ECDH_ANON: if (ecdh == null) { throw new SSLProtocolException ("Server did not send a ECDH Server Key Exchange message"); } m2 = new ECDHClientKeyExchange(ecdh.getPublicKey()); break; case K_ECDH_RSA: case K_ECDH_ECDSA: if (serverKey == null) { throw new SSLProtocolException ("Server did not send certificate message"); } if (serverKey instanceof ECPublicKey == false) { throw new SSLProtocolException ("Server certificate does not include an EC key"); } ECParameterSpec params = ((ECPublicKey)serverKey).getParams(); ecdh = new ECDHCrypt(params, sslContext.getSecureRandom()); m2 = new ECDHClientKeyExchange(ecdh.getPublicKey()); break; case K_KRB5: case K_KRB5_EXPORT: String hostname = getHostSE(); if (hostname == null) { throw new IOException("Hostname is required" + " to use Kerberos cipher suites"); } KerberosClientKeyExchange kerberosMsg = new KerberosClientKeyExchange( hostname, isLoopbackSE(), getAccSE(), protocolVersion, sslContext.getSecureRandom()); // Record the principals involved in exchange session.setPeerPrincipal(kerberosMsg.getPeerPrincipal()); session.setLocalPrincipal(kerberosMsg.getLocalPrincipal()); m2 = kerberosMsg; break; default: // somethings very wrong throw new RuntimeException ("Unsupported key exchange: " + keyExchange); } if (debug != null && Debug.isOn("handshake")) { m2.print(System.out); } m2.write(output); /* * THIRD, send a "change_cipher_spec" record followed by the * "Finished" message. We flush the messages we've queued up, to * get concurrency between client and server. The concurrency is * useful as we calculate the master secret, which is needed both * to compute the "Finished" message, and to compute the keys used * to protect all records following the change_cipher_spec. */ output.doHashes(); output.flush(); /* * We deferred calculating the master secret and this connection's * keying data; we do it now. Deferring this calculation is good * from a performance point of view, since it lets us do it during * some time that network delays and the server's own calculations * would otherwise cause to be "dead" in the critical path. */ SecretKey preMasterSecret; switch (keyExchange) { case K_RSA: case K_RSA_EXPORT: preMasterSecret = ((RSAClientKeyExchange)m2).preMaster; break; case K_KRB5: case K_KRB5_EXPORT: byte[] secretBytes = ((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret(); preMasterSecret = new SecretKeySpec(secretBytes, "TlsPremasterSecret"); break; case K_DHE_RSA: case K_DHE_DSS: case K_DH_ANON: preMasterSecret = dh.getAgreedSecret(serverDH); break; case K_ECDHE_RSA: case K_ECDHE_ECDSA: case K_ECDH_ANON: preMasterSecret = ecdh.getAgreedSecret(ephemeralServerKey); break; case K_ECDH_RSA: case K_ECDH_ECDSA: preMasterSecret = ecdh.getAgreedSecret(serverKey); break; default: throw new IOException("Internal error: unknown key exchange " + keyExchange); } calculateKeys(preMasterSecret, null); /* * FOURTH, if we sent a Certificate, we need to send a signed * CertificateVerify (unless the key in the client's certificate * was a Diffie-Hellman key).). * * This uses a hash of the previous handshake messages ... either * a nonfinal one (if the particular implementation supports it) * or else using the third element in the arrays of hashes being * computed. */ if (signingKey != null) { CertificateVerify m3; try { SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { preferableSignatureAlgorithm = SignatureAndHashAlgorithm.getPreferableAlgorithm( peerSupportedSignAlgs, signingKey.getAlgorithm()); if (preferableSignatureAlgorithm == null) { throw new SSLHandshakeException( "No supported signature algorithm"); } String hashAlg = SignatureAndHashAlgorithm.getHashAlgorithmName( preferableSignatureAlgorithm); if (hashAlg == null || hashAlg.length() == 0) { throw new SSLHandshakeException( "No supported hash algorithm"); } handshakeHash.setCertificateVerifyAlg(hashAlg); } m3 = new CertificateVerify(protocolVersion, handshakeHash, signingKey, session.getMasterSecret(), sslContext.getSecureRandom(), preferableSignatureAlgorithm); } catch (GeneralSecurityException e) { fatalSE(Alerts.alert_handshake_failure, "Error signing certificate verify", e); // NOTREACHED, make compiler happy m3 = null; } if (debug != null && Debug.isOn("handshake")) { m3.print(System.out); } m3.write(output); output.doHashes(); } else { if (protocolVersion.v >= ProtocolVersion.TLS12.v) { handshakeHash.setCertificateVerifyAlg(null); } } /* * OK, that's that!Lines 501–1000 of 1341. Previous lines Load more
Principal localPrincipal = session.getLocalPrincipal(); Subject subject = null; try { subject = AccessController.doPrivileged( new PrivilegedExceptionAction<Subject>() { public Subject run() throws Exception { return Krb5Helper.getClientSubject(getAccSE()); }}); } catch (PrivilegedActionException e) { subject = null; if (debug != null && Debug.isOn("session")) { System.out.println("Attempt to obtain" + " subject failed!"); } } if (subject != null) { // Eliminate dependency on KerberosPrincipal Set<Principal> principals = subject.getPrincipals(Principal.class); if (!principals.contains(localPrincipal)) { throw new SSLProtocolException("Server resumed" + " session with wrong subject identity"); } else { if (debug != null && Debug.isOn("session")) System.out.println("Subject identity is same"); } } else { if (debug != null && Debug.isOn("session")) System.out.println("Kerberos credentials are not" + " present in the current Subject; check if " + " javax.security.auth.useSubjectAsCreds" + " system property has been set to false"); throw new SSLProtocolException ("Server resumed session with no subject"); } } // looks fine; resume it, and update the state machine. resumingSession = true; state = HandshakeMessage.ht_finished - 1; calculateConnectionKeys(session.getMasterSecret()); if (debug != null && Debug.isOn("session")) { System.out.println("%% Server resumed " + session); } } else { // we wanted to resume, but the server refused session = null; if (!enableNewSession) { throw new SSLException ("New session creation is disabled"); } } } if (resumingSession && session != null) { if (protocolVersion.v >= ProtocolVersion.TLS12.v) { handshakeHash.setCertificateVerifyAlg(null); } setHandshakeSessionSE(session); return; } // check extensions for (HelloExtension ext : mesg.extensions.list()) { ExtensionType type = ext.type; if ((type != ExtensionType.EXT_ELLIPTIC_CURVES) && (type != ExtensionType.EXT_EC_POINT_FORMATS) && (type != ExtensionType.EXT_SERVER_NAME) && (type != ExtensionType.EXT_RENEGOTIATION_INFO)) { fatalSE(Alerts.alert_unsupported_extension, "Server sent an unsupported extension: " + type); } } // Create a new session, we need to do the full handshake session = new SSLSessionImpl(protocolVersion, cipherSuite, getLocalSupportedSignAlgs(), mesg.sessionId, getHostSE(), getPortSE()); setHandshakeSessionSE(session); if (debug != null && Debug.isOn("handshake")) { System.out.println("** " + cipherSuite); } } /* * Server's own key was either a signing-only key, or was too * large for export rules ... this message holds an ephemeral * RSA key to use for key exchange. */ private void serverKeyExchange(RSA_ServerKeyExchange mesg) throws IOException, GeneralSecurityException { if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } if (!mesg.verify(serverKey, clnt_random, svr_random)) { fatalSE(Alerts.alert_handshake_failure, "server key exchange invalid"); // NOTREACHED } ephemeralServerKey = mesg.getPublicKey(); } /* * Diffie-Hellman key exchange. We save the server public key and * our own D-H algorithm object so we can defer key calculations * until after we've sent the client key exchange message (which * gives client and server some useful parallelism). */ private void serverKeyExchange(DH_ServerKeyExchange mesg) throws IOException { if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } dh = new DHCrypt(mesg.getModulus(), mesg.getBase(), sslContext.getSecureRandom()); serverDH = mesg.getServerPublicKey(); } private void serverKeyExchange(ECDH_ServerKeyExchange mesg) throws IOException { if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } ECPublicKey key = mesg.getPublicKey(); ecdh = new ECDHCrypt(key.getParams(), sslContext.getSecureRandom()); ephemeralServerKey = key; } /* * The server's "Hello Done" message is the client's sign that * it's time to do all the hard work. */ private void serverHelloDone(ServerHelloDone mesg) throws IOException { if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } /* * Always make sure the input has been digested before we * start emitting data, to ensure the hashes are correctly * computed for the Finished and CertificateVerify messages * which we send (here). */ input.digestNow(); /* * FIRST ... if requested, send an appropriate Certificate chain * to authenticate the client, and remember the associated private * key to sign the CertificateVerify message. */ PrivateKey signingKey = null; if (certRequest != null) { X509ExtendedKeyManager km = sslContext.getX509KeyManager(); ArrayList<String> keytypesTmp = new ArrayList<>(4); for (int i = 0; i < certRequest.types.length; i++) { String typeName; switch (certRequest.types[i]) { case CertificateRequest.cct_rsa_sign: typeName = "RSA"; break; case CertificateRequest.cct_dss_sign: typeName = "DSA"; break; case CertificateRequest.cct_ecdsa_sign: // ignore if we do not have EC crypto available typeName = JsseJce.isEcAvailable() ? "EC" : null; break; // Fixed DH/ECDH client authentication not supported case CertificateRequest.cct_rsa_fixed_dh: case CertificateRequest.cct_dss_fixed_dh: case CertificateRequest.cct_rsa_fixed_ecdh: case CertificateRequest.cct_ecdsa_fixed_ecdh: // Any other values (currently not used in TLS) case CertificateRequest.cct_rsa_ephemeral_dh: case CertificateRequest.cct_dss_ephemeral_dh: default: typeName = null; break; } if ((typeName != null) && (!keytypesTmp.contains(typeName))) { keytypesTmp.add(typeName); } } String alias = null; int keytypesTmpSize = keytypesTmp.size(); if (keytypesTmpSize != 0) { String keytypes[] = keytypesTmp.toArray(new String[keytypesTmpSize]); if (conn != null) { alias = km.chooseClientAlias(keytypes, certRequest.getAuthorities(), conn); } else { alias = km.chooseEngineClientAlias(keytypes, certRequest.getAuthorities(), engine); } } CertificateMsg m1 = null; if (alias != null) { X509Certificate[] certs = km.getCertificateChain(alias); if ((certs != null) && (certs.length != 0)) { PublicKey publicKey = certs[0].getPublicKey(); // for EC, make sure we use a supported named curve if (publicKey instanceof ECPublicKey) { ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); int index = SupportedEllipticCurvesExtension.getCurveIndex( params); if (!SupportedEllipticCurvesExtension.isSupported( index)) { publicKey = null; } } if (publicKey != null) { m1 = new CertificateMsg(certs); signingKey = km.getPrivateKey(alias); session.setLocalPrivateKey(signingKey); session.setLocalCertificates(certs); } } } if (m1 == null) { // // No appropriate cert was found ... report this to the // server. For SSLv3, send the no_certificate alert; // TLS uses an empty cert chain instead. // if (protocolVersion.v >= ProtocolVersion.TLS10.v) { m1 = new CertificateMsg(new X509Certificate [0]); } else { warningSE(Alerts.alert_no_certificate); } } // // At last ... send any client certificate chain. // if (m1 != null) { if (debug != null && Debug.isOn("handshake")) { m1.print(System.out); } m1.write(output); } } /* * SECOND ... send the client key exchange message. The * procedure used is a function of the cipher suite selected; * one is always needed. */ HandshakeMessage m2; switch (keyExchange) { case K_RSA: case K_RSA_EXPORT: if (serverKey == null) { throw new SSLProtocolException ("Server did not send certificate message"); } if (!(serverKey instanceof RSAPublicKey)) { throw new SSLProtocolException ("Server certificate does not include an RSA key"); } /* * For RSA key exchange, we randomly generate a new * pre-master secret and encrypt it with the server's * public key. Then we save that pre-master secret * so that we can calculate the keying data later; * it's a performance speedup not to do that until * the client's waiting for the server response, but * more of a speedup for the D-H case. * * If the RSA_EXPORT scheme is active, when the public * key in the server certificate is less than or equal * to 512 bits in length, use the cert's public key, * otherwise, the ephemeral one. */ PublicKey key; if (keyExchange == K_RSA) { key = serverKey; } else { // K_RSA_EXPORT if (JsseJce.getRSAKeyLength(serverKey) <= 512) { // extraneous ephemeralServerKey check done // above in processMessage() key = serverKey; } else { if (ephemeralServerKey == null) { throw new SSLProtocolException("Server did not send" + " a RSA_EXPORT Server Key Exchange message"); } key = ephemeralServerKey; } } m2 = new RSAClientKeyExchange(protocolVersion, maxProtocolVersion, sslContext.getSecureRandom(), key); break; case K_DH_RSA: case K_DH_DSS: /* * For DH Key exchange, we only need to make sure the server * knows our public key, so we calculate the same pre-master * secret. * * For certs that had DH keys in them, we send an empty * handshake message (no key) ... we flag this case by * passing a null "dhPublic" value. * * Otherwise we send ephemeral DH keys, unsigned. */ // if (useDH_RSA || useDH_DSS) m2 = new DHClientKeyExchange(); break; case K_DHE_RSA: case K_DHE_DSS: case K_DH_ANON: if (dh == null) { throw new SSLProtocolException ("Server did not send a DH Server Key Exchange message"); } m2 = new DHClientKeyExchange(dh.getPublicKey()); break; case K_ECDHE_RSA: case K_ECDHE_ECDSA: case K_ECDH_ANON: if (ecdh == null) { throw new SSLProtocolException ("Server did not send a ECDH Server Key Exchange message"); } m2 = new ECDHClientKeyExchange(ecdh.getPublicKey()); break; case K_ECDH_RSA: case K_ECDH_ECDSA: if (serverKey == null) { throw new SSLProtocolException ("Server did not send certificate message"); } if (serverKey instanceof ECPublicKey == false) { throw new SSLProtocolException ("Server certificate does not include an EC key"); } ECParameterSpec params = ((ECPublicKey)serverKey).getParams(); ecdh = new ECDHCrypt(params, sslContext.getSecureRandom()); m2 = new ECDHClientKeyExchange(ecdh.getPublicKey()); break; case K_KRB5: case K_KRB5_EXPORT: String hostname = getHostSE(); if (hostname == null) { throw new IOException("Hostname is required" + " to use Kerberos cipher suites"); } KerberosClientKeyExchange kerberosMsg = new KerberosClientKeyExchange( hostname, isLoopbackSE(), getAccSE(), protocolVersion, sslContext.getSecureRandom()); // Record the principals involved in exchange session.setPeerPrincipal(kerberosMsg.getPeerPrincipal()); session.setLocalPrincipal(kerberosMsg.getLocalPrincipal()); m2 = kerberosMsg; break; default: // somethings very wrong throw new RuntimeException ("Unsupported key exchange: " + keyExchange); } if (debug != null && Debug.isOn("handshake")) { m2.print(System.out); } m2.write(output); /* * THIRD, send a "change_cipher_spec" record followed by the * "Finished" message. We flush the messages we've queued up, to * get concurrency between client and server. The concurrency is * useful as we calculate the master secret, which is needed both * to compute the "Finished" message, and to compute the keys used * to protect all records following the change_cipher_spec. */ output.doHashes(); output.flush(); /* * We deferred calculating the master secret and this connection's * keying data; we do it now. Deferring this calculation is good * from a performance point of view, since it lets us do it during * some time that network delays and the server's own calculations * would otherwise cause to be "dead" in the critical path. */ SecretKey preMasterSecret; switch (keyExchange) { case K_RSA: case K_RSA_EXPORT: preMasterSecret = ((RSAClientKeyExchange)m2).preMaster; break; case K_KRB5: case K_KRB5_EXPORT: byte[] secretBytes = ((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret(); preMasterSecret = new SecretKeySpec(secretBytes, "TlsPremasterSecret"); break; case K_DHE_RSA: case K_DHE_DSS: case K_DH_ANON: preMasterSecret = dh.getAgreedSecret(serverDH); break; case K_ECDHE_RSA: case K_ECDHE_ECDSA: case K_ECDH_ANON: preMasterSecret = ecdh.getAgreedSecret(ephemeralServerKey); break; case K_ECDH_RSA: case K_ECDH_ECDSA: preMasterSecret = ecdh.getAgreedSecret(serverKey); break; default: throw new IOException("Internal error: unknown key exchange " + keyExchange); } calculateKeys(preMasterSecret, null); /* * FOURTH, if we sent a Certificate, we need to send a signed * CertificateVerify (unless the key in the client's certificate * was a Diffie-Hellman key).). * * This uses a hash of the previous handshake messages ... either * a nonfinal one (if the particular implementation supports it) * or else using the third element in the arrays of hashes being * computed. */ if (signingKey != null) { CertificateVerify m3; try { SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { preferableSignatureAlgorithm = SignatureAndHashAlgorithm.getPreferableAlgorithm( peerSupportedSignAlgs, signingKey.getAlgorithm()); if (preferableSignatureAlgorithm == null) { throw new SSLHandshakeException( "No supported signature algorithm"); } String hashAlg = SignatureAndHashAlgorithm.getHashAlgorithmName( preferableSignatureAlgorithm); if (hashAlg == null || hashAlg.length() == 0) { throw new SSLHandshakeException( "No supported hash algorithm"); } handshakeHash.setCertificateVerifyAlg(hashAlg); } m3 = new CertificateVerify(protocolVersion, handshakeHash, signingKey, session.getMasterSecret(), sslContext.getSecureRandom(), preferableSignatureAlgorithm); } catch (GeneralSecurityException e) { fatalSE(Alerts.alert_handshake_failure, "Error signing certificate verify", e); // NOTREACHED, make compiler happy m3 = null; } if (debug != null && Debug.isOn("handshake")) { m3.print(System.out); } m3.write(output); output.doHashes(); } else { if (protocolVersion.v >= ProtocolVersion.TLS12.v) { handshakeHash.setCertificateVerifyAlg(null); } } /* * OK, that's that!