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