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