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