2
|
1 |
/*
|
6856
|
2 |
* Copyright (c) 1996, 2010, 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 |
package sun.security.ssl;
|
|
27 |
|
|
28 |
import java.io.*;
|
|
29 |
import java.math.BigInteger;
|
|
30 |
import java.security.*;
|
|
31 |
import java.util.*;
|
|
32 |
|
|
33 |
import java.security.interfaces.ECPublicKey;
|
703
|
34 |
import java.security.interfaces.RSAPublicKey;
|
2
|
35 |
import java.security.spec.ECParameterSpec;
|
|
36 |
|
|
37 |
import java.security.cert.X509Certificate;
|
|
38 |
import java.security.cert.CertificateException;
|
|
39 |
|
|
40 |
import javax.crypto.SecretKey;
|
|
41 |
import javax.crypto.spec.SecretKeySpec;
|
|
42 |
|
|
43 |
import javax.net.ssl.*;
|
|
44 |
|
|
45 |
import javax.security.auth.Subject;
|
|
46 |
|
|
47 |
import sun.security.ssl.HandshakeMessage.*;
|
|
48 |
import sun.security.ssl.CipherSuite.*;
|
|
49 |
import static sun.security.ssl.CipherSuite.KeyExchange.*;
|
|
50 |
|
7043
|
51 |
import sun.net.util.IPAddressUtil;
|
|
52 |
|
2
|
53 |
/**
|
|
54 |
* ClientHandshaker does the protocol handshaking from the point
|
|
55 |
* of view of a client. It is driven asychronously by handshake messages
|
|
56 |
* as delivered by the parent Handshaker class, and also uses
|
|
57 |
* common functionality (e.g. key generation) that is provided there.
|
|
58 |
*
|
|
59 |
* @author David Brownell
|
|
60 |
*/
|
|
61 |
final class ClientHandshaker extends Handshaker {
|
|
62 |
|
|
63 |
// the server's public key from its certificate.
|
|
64 |
private PublicKey serverKey;
|
|
65 |
|
|
66 |
// the server's ephemeral public key from the server key exchange message
|
|
67 |
// for ECDHE/ECDH_anon and RSA_EXPORT.
|
|
68 |
private PublicKey ephemeralServerKey;
|
|
69 |
|
|
70 |
// server's ephemeral public value for DHE/DH_anon key exchanges
|
|
71 |
private BigInteger serverDH;
|
|
72 |
|
|
73 |
private DHCrypt dh;
|
|
74 |
|
|
75 |
private ECDHCrypt ecdh;
|
|
76 |
|
|
77 |
private CertificateRequest certRequest;
|
|
78 |
|
|
79 |
private boolean serverKeyExchangeReceived;
|
|
80 |
|
|
81 |
/*
|
|
82 |
* The RSA PreMasterSecret needs to know the version of
|
|
83 |
* ClientHello that was used on this handshake. This represents
|
|
84 |
* the "max version" this client is supporting. In the
|
|
85 |
* case of an initial handshake, it's the max version enabled,
|
|
86 |
* but in the case of a resumption attempt, it's the version
|
|
87 |
* of the session we're trying to resume.
|
|
88 |
*/
|
|
89 |
private ProtocolVersion maxProtocolVersion;
|
|
90 |
|
7043
|
91 |
// To switch off the SNI extension.
|
|
92 |
private final static boolean enableSNIExtension =
|
|
93 |
Debug.getBooleanProperty("jsse.enableSNIExtension", true);
|
|
94 |
|
2
|
95 |
/*
|
|
96 |
* Constructors
|
|
97 |
*/
|
|
98 |
ClientHandshaker(SSLSocketImpl socket, SSLContextImpl context,
|
5182
|
99 |
ProtocolList enabledProtocols,
|
6856
|
100 |
ProtocolVersion activeProtocolVersion,
|
|
101 |
boolean isInitialHandshake, boolean secureRenegotiation,
|
|
102 |
byte[] clientVerifyData, byte[] serverVerifyData) {
|
|
103 |
|
|
104 |
super(socket, context, enabledProtocols, true, true,
|
|
105 |
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
|
|
106 |
clientVerifyData, serverVerifyData);
|
2
|
107 |
}
|
|
108 |
|
|
109 |
ClientHandshaker(SSLEngineImpl engine, SSLContextImpl context,
|
5182
|
110 |
ProtocolList enabledProtocols,
|
6856
|
111 |
ProtocolVersion activeProtocolVersion,
|
|
112 |
boolean isInitialHandshake, boolean secureRenegotiation,
|
|
113 |
byte[] clientVerifyData, byte[] serverVerifyData) {
|
|
114 |
|
|
115 |
super(engine, context, enabledProtocols, true, true,
|
|
116 |
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
|
|
117 |
clientVerifyData, serverVerifyData);
|
2
|
118 |
}
|
|
119 |
|
|
120 |
/*
|
|
121 |
* This routine handles all the client side handshake messages, one at
|
|
122 |
* a time. Given the message type (and in some cases the pending cipher
|
|
123 |
* spec) it parses the type-specific message. Then it calls a function
|
|
124 |
* that handles that specific message.
|
|
125 |
*
|
|
126 |
* It updates the state machine (need to verify it) as each message
|
|
127 |
* is processed, and writes responses as needed using the connection
|
|
128 |
* in the constructor.
|
|
129 |
*/
|
|
130 |
void processMessage(byte type, int messageLen) throws IOException {
|
|
131 |
if (state > type
|
|
132 |
&& (type != HandshakeMessage.ht_hello_request
|
|
133 |
&& state != HandshakeMessage.ht_client_hello)) {
|
|
134 |
throw new SSLProtocolException(
|
|
135 |
"Handshake message sequence violation, " + type);
|
|
136 |
}
|
|
137 |
|
|
138 |
switch (type) {
|
|
139 |
case HandshakeMessage.ht_hello_request:
|
|
140 |
this.serverHelloRequest(new HelloRequest(input));
|
|
141 |
break;
|
|
142 |
|
|
143 |
case HandshakeMessage.ht_server_hello:
|
|
144 |
this.serverHello(new ServerHello(input, messageLen));
|
|
145 |
break;
|
|
146 |
|
|
147 |
case HandshakeMessage.ht_certificate:
|
|
148 |
if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON
|
|
149 |
|| keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
|
|
150 |
fatalSE(Alerts.alert_unexpected_message,
|
|
151 |
"unexpected server cert chain");
|
|
152 |
// NOTREACHED
|
|
153 |
}
|
|
154 |
this.serverCertificate(new CertificateMsg(input));
|
|
155 |
serverKey =
|
|
156 |
session.getPeerCertificates()[0].getPublicKey();
|
|
157 |
break;
|
|
158 |
|
|
159 |
case HandshakeMessage.ht_server_key_exchange:
|
|
160 |
serverKeyExchangeReceived = true;
|
|
161 |
switch (keyExchange) {
|
|
162 |
case K_RSA_EXPORT:
|
703
|
163 |
/**
|
|
164 |
* The server key exchange message is sent by the server only
|
|
165 |
* when the server certificate message does not contain the
|
|
166 |
* proper amount of data to allow the client to exchange a
|
|
167 |
* premaster secret, such as when RSA_EXPORT is used and the
|
|
168 |
* public key in the server certificate is longer than 512 bits.
|
|
169 |
*/
|
|
170 |
if (serverKey == null) {
|
|
171 |
throw new SSLProtocolException
|
|
172 |
("Server did not send certificate message");
|
|
173 |
}
|
|
174 |
|
|
175 |
if (!(serverKey instanceof RSAPublicKey)) {
|
|
176 |
throw new SSLProtocolException("Protocol violation:" +
|
|
177 |
" the certificate type must be appropriate for the" +
|
|
178 |
" selected cipher suite's key exchange algorithm");
|
|
179 |
}
|
|
180 |
|
|
181 |
if (JsseJce.getRSAKeyLength(serverKey) <= 512) {
|
|
182 |
throw new SSLProtocolException("Protocol violation:" +
|
|
183 |
" server sent a server key exchange message for" +
|
|
184 |
" key exchange " + keyExchange +
|
|
185 |
" when the public key in the server certificate" +
|
|
186 |
" is less than or equal to 512 bits in length");
|
|
187 |
}
|
|
188 |
|
2
|
189 |
try {
|
|
190 |
this.serverKeyExchange(new RSA_ServerKeyExchange(input));
|
|
191 |
} catch (GeneralSecurityException e) {
|
|
192 |
throwSSLException("Server key", e);
|
|
193 |
}
|
|
194 |
break;
|
|
195 |
case K_DH_ANON:
|
7043
|
196 |
this.serverKeyExchange(new DH_ServerKeyExchange(
|
|
197 |
input, protocolVersion));
|
2
|
198 |
break;
|
|
199 |
case K_DHE_DSS:
|
|
200 |
case K_DHE_RSA:
|
|
201 |
try {
|
|
202 |
this.serverKeyExchange(new DH_ServerKeyExchange(
|
|
203 |
input, serverKey,
|
|
204 |
clnt_random.random_bytes, svr_random.random_bytes,
|
7043
|
205 |
messageLen,
|
|
206 |
localSupportedSignAlgs, protocolVersion));
|
2
|
207 |
} catch (GeneralSecurityException e) {
|
|
208 |
throwSSLException("Server key", e);
|
|
209 |
}
|
|
210 |
break;
|
|
211 |
case K_ECDHE_ECDSA:
|
|
212 |
case K_ECDHE_RSA:
|
|
213 |
case K_ECDH_ANON:
|
|
214 |
try {
|
|
215 |
this.serverKeyExchange(new ECDH_ServerKeyExchange
|
|
216 |
(input, serverKey, clnt_random.random_bytes,
|
7043
|
217 |
svr_random.random_bytes,
|
|
218 |
localSupportedSignAlgs, protocolVersion));
|
2
|
219 |
} catch (GeneralSecurityException e) {
|
|
220 |
throwSSLException("Server key", e);
|
|
221 |
}
|
|
222 |
break;
|
703
|
223 |
case K_RSA:
|
|
224 |
case K_DH_RSA:
|
|
225 |
case K_DH_DSS:
|
2
|
226 |
case K_ECDH_ECDSA:
|
|
227 |
case K_ECDH_RSA:
|
7043
|
228 |
throw new SSLProtocolException(
|
|
229 |
"Protocol violation: server sent a server key exchange"
|
|
230 |
+ "message for key exchange " + keyExchange);
|
2
|
231 |
case K_KRB5:
|
|
232 |
case K_KRB5_EXPORT:
|
|
233 |
throw new SSLProtocolException(
|
|
234 |
"unexpected receipt of server key exchange algorithm");
|
|
235 |
default:
|
|
236 |
throw new SSLProtocolException(
|
|
237 |
"unsupported key exchange algorithm = "
|
|
238 |
+ keyExchange);
|
|
239 |
}
|
|
240 |
break;
|
|
241 |
|
|
242 |
case HandshakeMessage.ht_certificate_request:
|
|
243 |
// save for later, it's handled by serverHelloDone
|
|
244 |
if ((keyExchange == K_DH_ANON) || (keyExchange == K_ECDH_ANON)) {
|
|
245 |
throw new SSLHandshakeException(
|
|
246 |
"Client authentication requested for "+
|
|
247 |
"anonymous cipher suite.");
|
|
248 |
} else if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
|
|
249 |
throw new SSLHandshakeException(
|
|
250 |
"Client certificate requested for "+
|
|
251 |
"kerberos cipher suite.");
|
|
252 |
}
|
7043
|
253 |
certRequest = new CertificateRequest(input, protocolVersion);
|
2
|
254 |
if (debug != null && Debug.isOn("handshake")) {
|
|
255 |
certRequest.print(System.out);
|
|
256 |
}
|
7043
|
257 |
|
|
258 |
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
|
|
259 |
Collection<SignatureAndHashAlgorithm> peerSignAlgs =
|
|
260 |
certRequest.getSignAlgorithms();
|
|
261 |
if (peerSignAlgs == null || peerSignAlgs.isEmpty()) {
|
|
262 |
throw new SSLHandshakeException(
|
|
263 |
"No peer supported signature algorithms");
|
|
264 |
}
|
|
265 |
|
|
266 |
Collection<SignatureAndHashAlgorithm> supportedPeerSignAlgs =
|
|
267 |
SignatureAndHashAlgorithm.getSupportedAlgorithms(
|
|
268 |
peerSignAlgs);
|
|
269 |
if (supportedPeerSignAlgs.isEmpty()) {
|
|
270 |
throw new SSLHandshakeException(
|
|
271 |
"No supported signature and hash algorithm in common");
|
|
272 |
}
|
|
273 |
|
|
274 |
setPeerSupportedSignAlgs(supportedPeerSignAlgs);
|
|
275 |
session.setPeerSupportedSignatureAlgorithms(
|
|
276 |
supportedPeerSignAlgs);
|
|
277 |
}
|
|
278 |
|
2
|
279 |
break;
|
|
280 |
|
|
281 |
case HandshakeMessage.ht_server_hello_done:
|
|
282 |
this.serverHelloDone(new ServerHelloDone(input));
|
|
283 |
break;
|
|
284 |
|
|
285 |
case HandshakeMessage.ht_finished:
|
7043
|
286 |
this.serverFinished(
|
|
287 |
new Finished(protocolVersion, input, cipherSuite));
|
2
|
288 |
break;
|
|
289 |
|
|
290 |
default:
|
|
291 |
throw new SSLProtocolException(
|
|
292 |
"Illegal client handshake msg, " + type);
|
|
293 |
}
|
|
294 |
|
|
295 |
//
|
|
296 |
// Move state machine forward if the message handling
|
|
297 |
// code didn't already do so
|
|
298 |
//
|
|
299 |
if (state < type) {
|
|
300 |
state = type;
|
|
301 |
}
|
|
302 |
}
|
|
303 |
|
|
304 |
/*
|
|
305 |
* Used by the server to kickstart negotiations -- this requests a
|
|
306 |
* "client hello" to renegotiate current cipher specs (e.g. maybe lots
|
|
307 |
* of data has been encrypted with the same keys, or the server needs
|
|
308 |
* the client to present a certificate).
|
|
309 |
*/
|
|
310 |
private void serverHelloRequest(HelloRequest mesg) throws IOException {
|
|
311 |
if (debug != null && Debug.isOn("handshake")) {
|
|
312 |
mesg.print(System.out);
|
|
313 |
}
|
|
314 |
|
|
315 |
//
|
|
316 |
// Could be (e.g. at connection setup) that we already
|
|
317 |
// sent the "client hello" but the server's not seen it.
|
|
318 |
//
|
|
319 |
if (state < HandshakeMessage.ht_client_hello) {
|
6856
|
320 |
if (!secureRenegotiation && !allowUnsafeRenegotiation) {
|
|
321 |
// renegotiation is not allowed.
|
5182
|
322 |
if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {
|
6856
|
323 |
// response with a no_renegotiation warning,
|
|
324 |
warningSE(Alerts.alert_no_renegotiation);
|
5182
|
325 |
|
|
326 |
// invalidate the handshake so that the caller can
|
|
327 |
// dispose this object.
|
|
328 |
invalidated = true;
|
|
329 |
|
|
330 |
// If there is still unread block in the handshake
|
|
331 |
// input stream, it would be truncated with the disposal
|
|
332 |
// and the next handshake message will become incomplete.
|
|
333 |
//
|
|
334 |
// However, according to SSL/TLS specifications, no more
|
6856
|
335 |
// handshake message should immediately follow ClientHello
|
|
336 |
// or HelloRequest. So just let it be.
|
5182
|
337 |
} else {
|
|
338 |
// For SSLv3, send the handshake_failure fatal error.
|
6856
|
339 |
// Note that SSLv3 does not define a no_renegotiation
|
|
340 |
// alert like TLSv1. However we cannot ignore the message
|
5182
|
341 |
// simply, otherwise the other side was waiting for a
|
|
342 |
// response that would never come.
|
|
343 |
fatalSE(Alerts.alert_handshake_failure,
|
6856
|
344 |
"Renegotiation is not allowed");
|
5182
|
345 |
}
|
|
346 |
} else {
|
6856
|
347 |
if (!secureRenegotiation) {
|
|
348 |
if (debug != null && Debug.isOn("handshake")) {
|
|
349 |
System.out.println(
|
|
350 |
"Warning: continue with insecure renegotiation");
|
|
351 |
}
|
|
352 |
}
|
5182
|
353 |
kickstart();
|
|
354 |
}
|
2
|
355 |
}
|
|
356 |
}
|
|
357 |
|
|
358 |
|
|
359 |
/*
|
|
360 |
* Server chooses session parameters given options created by the
|
|
361 |
* client -- basically, cipher options, session id, and someday a
|
|
362 |
* set of compression options.
|
|
363 |
*
|
|
364 |
* There are two branches of the state machine, decided by the
|
|
365 |
* details of this message. One is the "fast" handshake, where we
|
|
366 |
* can resume the pre-existing session we asked resume. The other
|
|
367 |
* is a more expensive "full" handshake, with key exchange and
|
|
368 |
* probably authentication getting done.
|
|
369 |
*/
|
|
370 |
private void serverHello(ServerHello mesg) throws IOException {
|
|
371 |
serverKeyExchangeReceived = false;
|
|
372 |
if (debug != null && Debug.isOn("handshake")) {
|
|
373 |
mesg.print(System.out);
|
|
374 |
}
|
|
375 |
|
|
376 |
// check if the server selected protocol version is OK for us
|
|
377 |
ProtocolVersion mesgVersion = mesg.protocolVersion;
|
7039
|
378 |
if (!isNegotiable(mesgVersion)) {
|
|
379 |
throw new SSLHandshakeException(
|
|
380 |
"Server chose unsupported or disabled protocol: " +
|
|
381 |
mesgVersion);
|
2
|
382 |
}
|
|
383 |
|
7804
|
384 |
handshakeHash.protocolDetermined(mesgVersion);
|
7043
|
385 |
|
2
|
386 |
// Set protocolVersion and propagate to SSLSocket and the
|
|
387 |
// Handshake streams
|
|
388 |
setVersion(mesgVersion);
|
|
389 |
|
6856
|
390 |
// check the "renegotiation_info" extension
|
|
391 |
RenegotiationInfoExtension serverHelloRI = (RenegotiationInfoExtension)
|
|
392 |
mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO);
|
|
393 |
if (serverHelloRI != null) {
|
|
394 |
if (isInitialHandshake) {
|
|
395 |
// verify the length of the "renegotiated_connection" field
|
|
396 |
if (!serverHelloRI.isEmpty()) {
|
|
397 |
// abort the handshake with a fatal handshake_failure alert
|
|
398 |
fatalSE(Alerts.alert_handshake_failure,
|
|
399 |
"The renegotiation_info field is not empty");
|
|
400 |
}
|
|
401 |
|
|
402 |
secureRenegotiation = true;
|
|
403 |
} else {
|
|
404 |
// For a legacy renegotiation, the client MUST verify that
|
|
405 |
// it does not contain the "renegotiation_info" extension.
|
|
406 |
if (!secureRenegotiation) {
|
|
407 |
fatalSE(Alerts.alert_handshake_failure,
|
|
408 |
"Unexpected renegotiation indication extension");
|
|
409 |
}
|
|
410 |
|
|
411 |
// verify the client_verify_data and server_verify_data values
|
|
412 |
byte[] verifyData =
|
|
413 |
new byte[clientVerifyData.length + serverVerifyData.length];
|
|
414 |
System.arraycopy(clientVerifyData, 0, verifyData,
|
|
415 |
0, clientVerifyData.length);
|
|
416 |
System.arraycopy(serverVerifyData, 0, verifyData,
|
|
417 |
clientVerifyData.length, serverVerifyData.length);
|
|
418 |
if (!Arrays.equals(verifyData,
|
|
419 |
serverHelloRI.getRenegotiatedConnection())) {
|
|
420 |
fatalSE(Alerts.alert_handshake_failure,
|
|
421 |
"Incorrect verify data in ServerHello " +
|
|
422 |
"renegotiation_info message");
|
|
423 |
}
|
|
424 |
}
|
|
425 |
} else {
|
|
426 |
// no renegotiation indication extension
|
|
427 |
if (isInitialHandshake) {
|
|
428 |
if (!allowLegacyHelloMessages) {
|
|
429 |
// abort the handshake with a fatal handshake_failure alert
|
|
430 |
fatalSE(Alerts.alert_handshake_failure,
|
|
431 |
"Failed to negotiate the use of secure renegotiation");
|
|
432 |
}
|
|
433 |
|
|
434 |
secureRenegotiation = false;
|
|
435 |
if (debug != null && Debug.isOn("handshake")) {
|
|
436 |
System.out.println("Warning: No renegotiation " +
|
|
437 |
"indication extension in ServerHello");
|
|
438 |
}
|
|
439 |
} else {
|
|
440 |
// For a secure renegotiation, the client must abort the
|
|
441 |
// handshake if no "renegotiation_info" extension is present.
|
|
442 |
if (secureRenegotiation) {
|
|
443 |
fatalSE(Alerts.alert_handshake_failure,
|
|
444 |
"No renegotiation indication extension");
|
|
445 |
}
|
|
446 |
|
|
447 |
// we have already allowed unsafe renegotation before request
|
|
448 |
// the renegotiation.
|
|
449 |
}
|
|
450 |
}
|
|
451 |
|
2
|
452 |
//
|
|
453 |
// Save server nonce, we always use it to compute connection
|
|
454 |
// keys and it's also used to create the master secret if we're
|
|
455 |
// creating a new session (i.e. in the full handshake).
|
|
456 |
//
|
|
457 |
svr_random = mesg.svr_random;
|
|
458 |
|
6856
|
459 |
if (isNegotiable(mesg.cipherSuite) == false) {
|
2
|
460 |
fatalSE(Alerts.alert_illegal_parameter,
|
7043
|
461 |
"Server selected improper ciphersuite " + mesg.cipherSuite);
|
2
|
462 |
}
|
6856
|
463 |
|
2
|
464 |
setCipherSuite(mesg.cipherSuite);
|
7043
|
465 |
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
|
|
466 |
handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
|
|
467 |
}
|
2
|
468 |
|
|
469 |
if (mesg.compression_method != 0) {
|
|
470 |
fatalSE(Alerts.alert_illegal_parameter,
|
|
471 |
"compression type not supported, "
|
|
472 |
+ mesg.compression_method);
|
|
473 |
// NOTREACHED
|
|
474 |
}
|
|
475 |
|
|
476 |
// so far so good, let's look at the session
|
|
477 |
if (session != null) {
|
|
478 |
// we tried to resume, let's see what the server decided
|
|
479 |
if (session.getSessionId().equals(mesg.sessionId)) {
|
|
480 |
// server resumed the session, let's make sure everything
|
|
481 |
// checks out
|
|
482 |
|
|
483 |
// Verify that the session ciphers are unchanged.
|
|
484 |
CipherSuite sessionSuite = session.getSuite();
|
|
485 |
if (cipherSuite != sessionSuite) {
|
|
486 |
throw new SSLProtocolException
|
|
487 |
("Server returned wrong cipher suite for session");
|
|
488 |
}
|
|
489 |
|
|
490 |
// verify protocol version match
|
|
491 |
ProtocolVersion sessionVersion = session.getProtocolVersion();
|
|
492 |
if (protocolVersion != sessionVersion) {
|
|
493 |
throw new SSLProtocolException
|
|
494 |
("Server resumed session with wrong protocol version");
|
|
495 |
}
|
|
496 |
|
|
497 |
// validate subject identity
|
|
498 |
if (sessionSuite.keyExchange == K_KRB5 ||
|
|
499 |
sessionSuite.keyExchange == K_KRB5_EXPORT) {
|
|
500 |
Principal localPrincipal = session.getLocalPrincipal();
|
|
501 |
|
|
502 |
Subject subject = null;
|
|
503 |
try {
|
|
504 |
subject = AccessController.doPrivileged(
|
|
505 |
new PrivilegedExceptionAction<Subject>() {
|
|
506 |
public Subject run() throws Exception {
|
4236
|
507 |
return Krb5Helper.getClientSubject(getAccSE());
|
2
|
508 |
}});
|
|
509 |
} catch (PrivilegedActionException e) {
|
|
510 |
subject = null;
|
|
511 |
if (debug != null && Debug.isOn("session")) {
|
|
512 |
System.out.println("Attempt to obtain" +
|
|
513 |
" subject failed!");
|
|
514 |
}
|
|
515 |
}
|
|
516 |
|
|
517 |
if (subject != null) {
|
4236
|
518 |
// Eliminate dependency on KerberosPrincipal
|
|
519 |
Set<Principal> principals =
|
|
520 |
subject.getPrincipals(Principal.class);
|
2
|
521 |
if (!principals.contains(localPrincipal)) {
|
|
522 |
throw new SSLProtocolException("Server resumed" +
|
|
523 |
" session with wrong subject identity");
|
|
524 |
} else {
|
|
525 |
if (debug != null && Debug.isOn("session"))
|
|
526 |
System.out.println("Subject identity is same");
|
|
527 |
}
|
|
528 |
} else {
|
|
529 |
if (debug != null && Debug.isOn("session"))
|
|
530 |
System.out.println("Kerberos credentials are not" +
|
|
531 |
" present in the current Subject; check if " +
|
|
532 |
" javax.security.auth.useSubjectAsCreds" +
|
|
533 |
" system property has been set to false");
|
|
534 |
throw new SSLProtocolException
|
|
535 |
("Server resumed session with no subject");
|
|
536 |
}
|
|
537 |
}
|
|
538 |
|
|
539 |
// looks fine; resume it, and update the state machine.
|
|
540 |
resumingSession = true;
|
|
541 |
state = HandshakeMessage.ht_finished - 1;
|
|
542 |
calculateConnectionKeys(session.getMasterSecret());
|
|
543 |
if (debug != null && Debug.isOn("session")) {
|
|
544 |
System.out.println("%% Server resumed " + session);
|
|
545 |
}
|
|
546 |
} else {
|
|
547 |
// we wanted to resume, but the server refused
|
|
548 |
session = null;
|
|
549 |
if (!enableNewSession) {
|
|
550 |
throw new SSLException
|
|
551 |
("New session creation is disabled");
|
|
552 |
}
|
|
553 |
}
|
|
554 |
}
|
|
555 |
|
7043
|
556 |
if (resumingSession && session != null) {
|
|
557 |
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
|
|
558 |
handshakeHash.setCertificateVerifyAlg(null);
|
|
559 |
}
|
|
560 |
|
|
561 |
setHandshakeSessionSE(session);
|
|
562 |
return;
|
|
563 |
}
|
|
564 |
|
2
|
565 |
// check extensions
|
|
566 |
for (HelloExtension ext : mesg.extensions.list()) {
|
|
567 |
ExtensionType type = ext.type;
|
|
568 |
if ((type != ExtensionType.EXT_ELLIPTIC_CURVES)
|
6856
|
569 |
&& (type != ExtensionType.EXT_EC_POINT_FORMATS)
|
7043
|
570 |
&& (type != ExtensionType.EXT_SERVER_NAME)
|
6856
|
571 |
&& (type != ExtensionType.EXT_RENEGOTIATION_INFO)) {
|
2
|
572 |
fatalSE(Alerts.alert_unsupported_extension,
|
|
573 |
"Server sent an unsupported extension: " + type);
|
|
574 |
}
|
|
575 |
}
|
|
576 |
|
|
577 |
// Create a new session, we need to do the full handshake
|
|
578 |
session = new SSLSessionImpl(protocolVersion, cipherSuite,
|
7043
|
579 |
getLocalSupportedSignAlgs(),
|
2
|
580 |
mesg.sessionId, getHostSE(), getPortSE());
|
7043
|
581 |
setHandshakeSessionSE(session);
|
2
|
582 |
if (debug != null && Debug.isOn("handshake")) {
|
|
583 |
System.out.println("** " + cipherSuite);
|
|
584 |
}
|
|
585 |
}
|
|
586 |
|
|
587 |
/*
|
|
588 |
* Server's own key was either a signing-only key, or was too
|
|
589 |
* large for export rules ... this message holds an ephemeral
|
|
590 |
* RSA key to use for key exchange.
|
|
591 |
*/
|
|
592 |
private void serverKeyExchange(RSA_ServerKeyExchange mesg)
|
|
593 |
throws IOException, GeneralSecurityException {
|
|
594 |
if (debug != null && Debug.isOn("handshake")) {
|
|
595 |
mesg.print(System.out);
|
|
596 |
}
|
|
597 |
if (!mesg.verify(serverKey, clnt_random, svr_random)) {
|
|
598 |
fatalSE(Alerts.alert_handshake_failure,
|
|
599 |
"server key exchange invalid");
|
|
600 |
// NOTREACHED
|
|
601 |
}
|
|
602 |
ephemeralServerKey = mesg.getPublicKey();
|
|
603 |
}
|
|
604 |
|
|
605 |
|
|
606 |
/*
|
|
607 |
* Diffie-Hellman key exchange. We save the server public key and
|
|
608 |
* our own D-H algorithm object so we can defer key calculations
|
|
609 |
* until after we've sent the client key exchange message (which
|
|
610 |
* gives client and server some useful parallelism).
|
|
611 |
*/
|
|
612 |
private void serverKeyExchange(DH_ServerKeyExchange mesg)
|
|
613 |
throws IOException {
|
|
614 |
if (debug != null && Debug.isOn("handshake")) {
|
|
615 |
mesg.print(System.out);
|
|
616 |
}
|
7043
|
617 |
dh = new DHCrypt(mesg.getModulus(), mesg.getBase(),
|
|
618 |
sslContext.getSecureRandom());
|
2
|
619 |
serverDH = mesg.getServerPublicKey();
|
|
620 |
}
|
|
621 |
|
7043
|
622 |
private void serverKeyExchange(ECDH_ServerKeyExchange mesg)
|
|
623 |
throws IOException {
|
2
|
624 |
if (debug != null && Debug.isOn("handshake")) {
|
|
625 |
mesg.print(System.out);
|
|
626 |
}
|
|
627 |
ECPublicKey key = mesg.getPublicKey();
|
|
628 |
ecdh = new ECDHCrypt(key.getParams(), sslContext.getSecureRandom());
|
|
629 |
ephemeralServerKey = key;
|
|
630 |
}
|
|
631 |
|
|
632 |
/*
|
|
633 |
* The server's "Hello Done" message is the client's sign that
|
|
634 |
* it's time to do all the hard work.
|
|
635 |
*/
|
|
636 |
private void serverHelloDone(ServerHelloDone mesg) throws IOException {
|
|
637 |
if (debug != null && Debug.isOn("handshake")) {
|
|
638 |
mesg.print(System.out);
|
|
639 |
}
|
|
640 |
/*
|
|
641 |
* Always make sure the input has been digested before we
|
|
642 |
* start emitting data, to ensure the hashes are correctly
|
|
643 |
* computed for the Finished and CertificateVerify messages
|
|
644 |
* which we send (here).
|
|
645 |
*/
|
|
646 |
input.digestNow();
|
|
647 |
|
|
648 |
/*
|
|
649 |
* FIRST ... if requested, send an appropriate Certificate chain
|
|
650 |
* to authenticate the client, and remember the associated private
|
|
651 |
* key to sign the CertificateVerify message.
|
|
652 |
*/
|
|
653 |
PrivateKey signingKey = null;
|
|
654 |
|
|
655 |
if (certRequest != null) {
|
|
656 |
X509ExtendedKeyManager km = sslContext.getX509KeyManager();
|
|
657 |
|
7990
|
658 |
ArrayList<String> keytypesTmp = new ArrayList<>(4);
|
2
|
659 |
|
|
660 |
for (int i = 0; i < certRequest.types.length; i++) {
|
|
661 |
String typeName;
|
|
662 |
|
|
663 |
switch (certRequest.types[i]) {
|
|
664 |
case CertificateRequest.cct_rsa_sign:
|
|
665 |
typeName = "RSA";
|
|
666 |
break;
|
|
667 |
|
|
668 |
case CertificateRequest.cct_dss_sign:
|
|
669 |
typeName = "DSA";
|
|
670 |
break;
|
|
671 |
|
|
672 |
case CertificateRequest.cct_ecdsa_sign:
|
|
673 |
// ignore if we do not have EC crypto available
|
|
674 |
typeName = JsseJce.isEcAvailable() ? "EC" : null;
|
|
675 |
break;
|
|
676 |
|
|
677 |
// Fixed DH/ECDH client authentication not supported
|
|
678 |
case CertificateRequest.cct_rsa_fixed_dh:
|
|
679 |
case CertificateRequest.cct_dss_fixed_dh:
|
|
680 |
case CertificateRequest.cct_rsa_fixed_ecdh:
|
|
681 |
case CertificateRequest.cct_ecdsa_fixed_ecdh:
|
|
682 |
// Any other values (currently not used in TLS)
|
|
683 |
case CertificateRequest.cct_rsa_ephemeral_dh:
|
|
684 |
case CertificateRequest.cct_dss_ephemeral_dh:
|
|
685 |
default:
|
|
686 |
typeName = null;
|
|
687 |
break;
|
|
688 |
}
|
|
689 |
|
|
690 |
if ((typeName != null) && (!keytypesTmp.contains(typeName))) {
|
|
691 |
keytypesTmp.add(typeName);
|
|
692 |
}
|
|
693 |
}
|
|
694 |
|
|
695 |
String alias = null;
|
|
696 |
int keytypesTmpSize = keytypesTmp.size();
|
|
697 |
if (keytypesTmpSize != 0) {
|
|
698 |
String keytypes[] =
|
|
699 |
keytypesTmp.toArray(new String[keytypesTmpSize]);
|
|
700 |
|
|
701 |
if (conn != null) {
|
|
702 |
alias = km.chooseClientAlias(keytypes,
|
|
703 |
certRequest.getAuthorities(), conn);
|
|
704 |
} else {
|
|
705 |
alias = km.chooseEngineClientAlias(keytypes,
|
|
706 |
certRequest.getAuthorities(), engine);
|
|
707 |
}
|
|
708 |
}
|
|
709 |
|
|
710 |
CertificateMsg m1 = null;
|
|
711 |
if (alias != null) {
|
|
712 |
X509Certificate[] certs = km.getCertificateChain(alias);
|
|
713 |
if ((certs != null) && (certs.length != 0)) {
|
|
714 |
PublicKey publicKey = certs[0].getPublicKey();
|
|
715 |
// for EC, make sure we use a supported named curve
|
|
716 |
if (publicKey instanceof ECPublicKey) {
|
7043
|
717 |
ECParameterSpec params =
|
|
718 |
((ECPublicKey)publicKey).getParams();
|
|
719 |
int index =
|
|
720 |
SupportedEllipticCurvesExtension.getCurveIndex(
|
|
721 |
params);
|
|
722 |
if (!SupportedEllipticCurvesExtension.isSupported(
|
|
723 |
index)) {
|
2
|
724 |
publicKey = null;
|
|
725 |
}
|
|
726 |
}
|
|
727 |
if (publicKey != null) {
|
|
728 |
m1 = new CertificateMsg(certs);
|
|
729 |
signingKey = km.getPrivateKey(alias);
|
|
730 |
session.setLocalPrivateKey(signingKey);
|
|
731 |
session.setLocalCertificates(certs);
|
|
732 |
}
|
|
733 |
}
|
|
734 |
}
|
|
735 |
if (m1 == null) {
|
|
736 |
//
|
|
737 |
// No appropriate cert was found ... report this to the
|
|
738 |
// server. For SSLv3, send the no_certificate alert;
|
|
739 |
// TLS uses an empty cert chain instead.
|
|
740 |
//
|
|
741 |
if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
|
|
742 |
m1 = new CertificateMsg(new X509Certificate [0]);
|
|
743 |
} else {
|
|
744 |
warningSE(Alerts.alert_no_certificate);
|
|
745 |
}
|
|
746 |
}
|
|
747 |
|
|
748 |
//
|
|
749 |
// At last ... send any client certificate chain.
|
|
750 |
//
|
|
751 |
if (m1 != null) {
|
|
752 |
if (debug != null && Debug.isOn("handshake")) {
|
|
753 |
m1.print(System.out);
|
|
754 |
}
|
|
755 |
m1.write(output);
|
|
756 |
}
|
|
757 |
}
|
|
758 |
|
|
759 |
/*
|
|
760 |
* SECOND ... send the client key exchange message. The
|
|
761 |
* procedure used is a function of the cipher suite selected;
|
|
762 |
* one is always needed.
|
|
763 |
*/
|
|
764 |
HandshakeMessage m2;
|
|
765 |
|
|
766 |
switch (keyExchange) {
|
|
767 |
|
|
768 |
case K_RSA:
|
|
769 |
case K_RSA_EXPORT:
|
703
|
770 |
if (serverKey == null) {
|
|
771 |
throw new SSLProtocolException
|
|
772 |
("Server did not send certificate message");
|
|
773 |
}
|
|
774 |
|
|
775 |
if (!(serverKey instanceof RSAPublicKey)) {
|
|
776 |
throw new SSLProtocolException
|
|
777 |
("Server certificate does not include an RSA key");
|
|
778 |
}
|
|
779 |
|
2
|
780 |
/*
|
|
781 |
* For RSA key exchange, we randomly generate a new
|
|
782 |
* pre-master secret and encrypt it with the server's
|
|
783 |
* public key. Then we save that pre-master secret
|
|
784 |
* so that we can calculate the keying data later;
|
|
785 |
* it's a performance speedup not to do that until
|
|
786 |
* the client's waiting for the server response, but
|
|
787 |
* more of a speedup for the D-H case.
|
703
|
788 |
*
|
|
789 |
* If the RSA_EXPORT scheme is active, when the public
|
|
790 |
* key in the server certificate is less than or equal
|
|
791 |
* to 512 bits in length, use the cert's public key,
|
|
792 |
* otherwise, the ephemeral one.
|
2
|
793 |
*/
|
703
|
794 |
PublicKey key;
|
|
795 |
if (keyExchange == K_RSA) {
|
|
796 |
key = serverKey;
|
|
797 |
} else { // K_RSA_EXPORT
|
|
798 |
if (JsseJce.getRSAKeyLength(serverKey) <= 512) {
|
|
799 |
// extraneous ephemeralServerKey check done
|
|
800 |
// above in processMessage()
|
|
801 |
key = serverKey;
|
|
802 |
} else {
|
|
803 |
if (ephemeralServerKey == null) {
|
|
804 |
throw new SSLProtocolException("Server did not send" +
|
|
805 |
" a RSA_EXPORT Server Key Exchange message");
|
|
806 |
}
|
|
807 |
key = ephemeralServerKey;
|
|
808 |
}
|
|
809 |
}
|
|
810 |
|
2
|
811 |
m2 = new RSAClientKeyExchange(protocolVersion, maxProtocolVersion,
|
|
812 |
sslContext.getSecureRandom(), key);
|
|
813 |
break;
|
|
814 |
case K_DH_RSA:
|
|
815 |
case K_DH_DSS:
|
|
816 |
/*
|
|
817 |
* For DH Key exchange, we only need to make sure the server
|
|
818 |
* knows our public key, so we calculate the same pre-master
|
|
819 |
* secret.
|
|
820 |
*
|
|
821 |
* For certs that had DH keys in them, we send an empty
|
|
822 |
* handshake message (no key) ... we flag this case by
|
|
823 |
* passing a null "dhPublic" value.
|
|
824 |
*
|
|
825 |
* Otherwise we send ephemeral DH keys, unsigned.
|
|
826 |
*/
|
|
827 |
// if (useDH_RSA || useDH_DSS)
|
|
828 |
m2 = new DHClientKeyExchange();
|
|
829 |
break;
|
|
830 |
case K_DHE_RSA:
|
|
831 |
case K_DHE_DSS:
|
|
832 |
case K_DH_ANON:
|
|
833 |
if (dh == null) {
|
|
834 |
throw new SSLProtocolException
|
|
835 |
("Server did not send a DH Server Key Exchange message");
|
|
836 |
}
|
|
837 |
m2 = new DHClientKeyExchange(dh.getPublicKey());
|
|
838 |
break;
|
|
839 |
case K_ECDHE_RSA:
|
|
840 |
case K_ECDHE_ECDSA:
|
|
841 |
case K_ECDH_ANON:
|
|
842 |
if (ecdh == null) {
|
|
843 |
throw new SSLProtocolException
|
|
844 |
("Server did not send a ECDH Server Key Exchange message");
|
|
845 |
}
|
|
846 |
m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
|
|
847 |
break;
|
|
848 |
case K_ECDH_RSA:
|
|
849 |
case K_ECDH_ECDSA:
|
|
850 |
if (serverKey == null) {
|
|
851 |
throw new SSLProtocolException
|
|
852 |
("Server did not send certificate message");
|
|
853 |
}
|
|
854 |
if (serverKey instanceof ECPublicKey == false) {
|
|
855 |
throw new SSLProtocolException
|
|
856 |
("Server certificate does not include an EC key");
|
|
857 |
}
|
|
858 |
ECParameterSpec params = ((ECPublicKey)serverKey).getParams();
|
|
859 |
ecdh = new ECDHCrypt(params, sslContext.getSecureRandom());
|
|
860 |
m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
|
|
861 |
break;
|
|
862 |
case K_KRB5:
|
|
863 |
case K_KRB5_EXPORT:
|
|
864 |
String hostname = getHostSE();
|
|
865 |
if (hostname == null) {
|
|
866 |
throw new IOException("Hostname is required" +
|
|
867 |
" to use Kerberos cipher suites");
|
|
868 |
}
|
7043
|
869 |
KerberosClientKeyExchange kerberosMsg =
|
|
870 |
new KerberosClientKeyExchange(
|
|
871 |
hostname, isLoopbackSE(), getAccSE(), protocolVersion,
|
2
|
872 |
sslContext.getSecureRandom());
|
|
873 |
// Record the principals involved in exchange
|
|
874 |
session.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
|
|
875 |
session.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
|
|
876 |
m2 = kerberosMsg;
|
|
877 |
break;
|
|
878 |
default:
|
|
879 |
// somethings very wrong
|
|
880 |
throw new RuntimeException
|
|
881 |
("Unsupported key exchange: " + keyExchange);
|
|
882 |
}
|
|
883 |
if (debug != null && Debug.isOn("handshake")) {
|
|
884 |
m2.print(System.out);
|
|
885 |
}
|
|
886 |
m2.write(output);
|
|
887 |
|
|
888 |
|
|
889 |
/*
|
|
890 |
* THIRD, send a "change_cipher_spec" record followed by the
|
|
891 |
* "Finished" message. We flush the messages we've queued up, to
|
|
892 |
* get concurrency between client and server. The concurrency is
|
|
893 |
* useful as we calculate the master secret, which is needed both
|
|
894 |
* to compute the "Finished" message, and to compute the keys used
|
|
895 |
* to protect all records following the change_cipher_spec.
|
|
896 |
*/
|
|
897 |
|
|
898 |
output.doHashes();
|
|
899 |
output.flush();
|
|
900 |
|
|
901 |
/*
|
|
902 |
* We deferred calculating the master secret and this connection's
|
|
903 |
* keying data; we do it now. Deferring this calculation is good
|
|
904 |
* from a performance point of view, since it lets us do it during
|
|
905 |
* some time that network delays and the server's own calculations
|
|
906 |
* would otherwise cause to be "dead" in the critical path.
|
|
907 |
*/
|
|
908 |
SecretKey preMasterSecret;
|
|
909 |
switch (keyExchange) {
|
|
910 |
case K_RSA:
|
|
911 |
case K_RSA_EXPORT:
|
|
912 |
preMasterSecret = ((RSAClientKeyExchange)m2).preMaster;
|
|
913 |
break;
|
|
914 |
case K_KRB5:
|
|
915 |
case K_KRB5_EXPORT:
|
|
916 |
byte[] secretBytes =
|
4236
|
917 |
((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret();
|
7043
|
918 |
preMasterSecret = new SecretKeySpec(secretBytes,
|
|
919 |
"TlsPremasterSecret");
|
2
|
920 |
break;
|
|
921 |
case K_DHE_RSA:
|
|
922 |
case K_DHE_DSS:
|
|
923 |
case K_DH_ANON:
|
|
924 |
preMasterSecret = dh.getAgreedSecret(serverDH);
|
|
925 |
break;
|
|
926 |
case K_ECDHE_RSA:
|
|
927 |
case K_ECDHE_ECDSA:
|
|
928 |
case K_ECDH_ANON:
|
|
929 |
preMasterSecret = ecdh.getAgreedSecret(ephemeralServerKey);
|
|
930 |
break;
|
|
931 |
case K_ECDH_RSA:
|
|
932 |
case K_ECDH_ECDSA:
|
|
933 |
preMasterSecret = ecdh.getAgreedSecret(serverKey);
|
|
934 |
break;
|
|
935 |
default:
|
7043
|
936 |
throw new IOException("Internal error: unknown key exchange "
|
|
937 |
+ keyExchange);
|
2
|
938 |
}
|
|
939 |
|
|
940 |
calculateKeys(preMasterSecret, null);
|
|
941 |
|
|
942 |
/*
|
|
943 |
* FOURTH, if we sent a Certificate, we need to send a signed
|
|
944 |
* CertificateVerify (unless the key in the client's certificate
|
|
945 |
* was a Diffie-Hellman key).).
|
|
946 |
*
|
|
947 |
* This uses a hash of the previous handshake messages ... either
|
|
948 |
* a nonfinal one (if the particular implementation supports it)
|
|
949 |
* or else using the third element in the arrays of hashes being
|
|
950 |
* computed.
|
|
951 |
*/
|
|
952 |
if (signingKey != null) {
|
|
953 |
CertificateVerify m3;
|
|
954 |
try {
|
7043
|
955 |
SignatureAndHashAlgorithm preferableSignatureAlgorithm = null;
|
|
956 |
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
|
|
957 |
preferableSignatureAlgorithm =
|
|
958 |
SignatureAndHashAlgorithm.getPreferableAlgorithm(
|
|
959 |
peerSupportedSignAlgs, signingKey.getAlgorithm());
|
|
960 |
|
|
961 |
if (preferableSignatureAlgorithm == null) {
|
|
962 |
throw new SSLHandshakeException(
|
|
963 |
"No supported signature algorithm");
|
|
964 |
}
|
|
965 |
|
|
966 |
String hashAlg =
|
|
967 |
SignatureAndHashAlgorithm.getHashAlgorithmName(
|
|
968 |
preferableSignatureAlgorithm);
|
|
969 |
if (hashAlg == null || hashAlg.length() == 0) {
|
|
970 |
throw new SSLHandshakeException(
|
|
971 |
"No supported hash algorithm");
|
|
972 |
}
|
|
973 |
|
|
974 |
handshakeHash.setCertificateVerifyAlg(hashAlg);
|
|
975 |
}
|
|
976 |
|
2
|
977 |
m3 = new CertificateVerify(protocolVersion, handshakeHash,
|
|
978 |
signingKey, session.getMasterSecret(),
|
7043
|
979 |
sslContext.getSecureRandom(),
|
|
980 |
preferableSignatureAlgorithm);
|
2
|
981 |
} catch (GeneralSecurityException e) {
|
|
982 |
fatalSE(Alerts.alert_handshake_failure,
|
|
983 |
"Error signing certificate verify", e);
|
|
984 |
// NOTREACHED, make compiler happy
|
|
985 |
m3 = null;
|
|
986 |
}
|
|
987 |
if (debug != null && Debug.isOn("handshake")) {
|
|
988 |
m3.print(System.out);
|
|
989 |
}
|
|
990 |
m3.write(output);
|
|
991 |
output.doHashes();
|
7043
|
992 |
} else {
|
|
993 |
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
|
|
994 |
handshakeHash.setCertificateVerifyAlg(null);
|
|
995 |
}
|
2
|
996 |
}
|
|
997 |
|
|
998 |
/*
|
|
999 |
* OK, that's that!
|
|
1000 |
*/
|
|
1001 |
sendChangeCipherAndFinish(false);
|
|
1002 |
}
|
|
1003 |
|
|
1004 |
|
|
1005 |
/*
|
|
1006 |
* "Finished" is the last handshake message sent. If we got this
|
|
1007 |
* far, the MAC has been validated post-decryption. We validate
|
|
1008 |
* the two hashes here as an additional sanity check, protecting
|
|
1009 |
* the handshake against various active attacks.
|
|
1010 |
*/
|
|
1011 |
private void serverFinished(Finished mesg) throws IOException {
|
|
1012 |
if (debug != null && Debug.isOn("handshake")) {
|
|
1013 |
mesg.print(System.out);
|
|
1014 |
}
|
|
1015 |
|
7043
|
1016 |
boolean verified = mesg.verify(handshakeHash, Finished.SERVER,
|
|
1017 |
session.getMasterSecret());
|
2
|
1018 |
|
|
1019 |
if (!verified) {
|
|
1020 |
fatalSE(Alerts.alert_illegal_parameter,
|
|
1021 |
"server 'finished' message doesn't verify");
|
|
1022 |
// NOTREACHED
|
|
1023 |
}
|
|
1024 |
|
|
1025 |
/*
|
6856
|
1026 |
* save server verify data for secure renegotiation
|
|
1027 |
*/
|
|
1028 |
if (secureRenegotiation) {
|
|
1029 |
serverVerifyData = mesg.getVerifyData();
|
|
1030 |
}
|
|
1031 |
|
|
1032 |
/*
|
2
|
1033 |
* OK, it verified. If we're doing the fast handshake, add that
|
|
1034 |
* "Finished" message to the hash of handshake messages, then send
|
|
1035 |
* our own change_cipher_spec and Finished message for the server
|
|
1036 |
* to verify in turn. These are the last handshake messages.
|
|
1037 |
*
|
|
1038 |
* In any case, update the session cache. We're done handshaking,
|
|
1039 |
* so there are no threats any more associated with partially
|
|
1040 |
* completed handshakes.
|
|
1041 |
*/
|
|
1042 |
if (resumingSession) {
|
|
1043 |
input.digestNow();
|
|
1044 |
sendChangeCipherAndFinish(true);
|
|
1045 |
}
|
|
1046 |
session.setLastAccessedTime(System.currentTimeMillis());
|
|
1047 |
|
|
1048 |
if (!resumingSession) {
|
|
1049 |
if (session.isRejoinable()) {
|
|
1050 |
((SSLSessionContextImpl) sslContext
|
|
1051 |
.engineGetClientSessionContext())
|
|
1052 |
.put(session);
|
|
1053 |
if (debug != null && Debug.isOn("session")) {
|
|
1054 |
System.out.println("%% Cached client session: " + session);
|
|
1055 |
}
|
|
1056 |
} else if (debug != null && Debug.isOn("session")) {
|
|
1057 |
System.out.println(
|
|
1058 |
"%% Didn't cache non-resumable client session: "
|
|
1059 |
+ session);
|
|
1060 |
}
|
|
1061 |
}
|
|
1062 |
}
|
|
1063 |
|
|
1064 |
|
|
1065 |
/*
|
|
1066 |
* Send my change-cipher-spec and Finished message ... done as the
|
|
1067 |
* last handshake act in either the short or long sequences. In
|
|
1068 |
* the short one, we've already seen the server's Finished; in the
|
|
1069 |
* long one, we wait for it now.
|
|
1070 |
*/
|
|
1071 |
private void sendChangeCipherAndFinish(boolean finishedTag)
|
|
1072 |
throws IOException {
|
|
1073 |
Finished mesg = new Finished(protocolVersion, handshakeHash,
|
7043
|
1074 |
Finished.CLIENT, session.getMasterSecret(), cipherSuite);
|
2
|
1075 |
|
|
1076 |
/*
|
|
1077 |
* Send the change_cipher_spec message, then the Finished message
|
|
1078 |
* which we just calculated (and protected using the keys we just
|
|
1079 |
* calculated). Server responds with its Finished message, except
|
|
1080 |
* in the "fast handshake" (resume session) case.
|
|
1081 |
*/
|
|
1082 |
sendChangeCipherSpec(mesg, finishedTag);
|
|
1083 |
|
|
1084 |
/*
|
6856
|
1085 |
* save client verify data for secure renegotiation
|
|
1086 |
*/
|
|
1087 |
if (secureRenegotiation) {
|
|
1088 |
clientVerifyData = mesg.getVerifyData();
|
|
1089 |
}
|
|
1090 |
|
|
1091 |
/*
|
2
|
1092 |
* Update state machine so server MUST send 'finished' next.
|
|
1093 |
* (In "long" handshake case; in short case, we're responding
|
|
1094 |
* to its message.)
|
|
1095 |
*/
|
|
1096 |
state = HandshakeMessage.ht_finished - 1;
|
|
1097 |
}
|
|
1098 |
|
|
1099 |
|
|
1100 |
/*
|
|
1101 |
* Returns a ClientHello message to kickstart renegotiations
|
|
1102 |
*/
|
|
1103 |
HandshakeMessage getKickstartMessage() throws SSLException {
|
6856
|
1104 |
// session ID of the ClientHello message
|
|
1105 |
SessionId sessionId = SSLSessionImpl.nullSession.getSessionId();
|
|
1106 |
|
|
1107 |
// a list of cipher suites sent by the client
|
7039
|
1108 |
CipherSuiteList cipherSuites = getActiveCipherSuites();
|
6856
|
1109 |
|
|
1110 |
// set the max protocol version this client is supporting.
|
2
|
1111 |
maxProtocolVersion = protocolVersion;
|
|
1112 |
|
|
1113 |
//
|
|
1114 |
// Try to resume an existing session. This might be mandatory,
|
|
1115 |
// given certain API options.
|
|
1116 |
//
|
|
1117 |
session = ((SSLSessionContextImpl)sslContext
|
|
1118 |
.engineGetClientSessionContext())
|
|
1119 |
.get(getHostSE(), getPortSE());
|
|
1120 |
if (debug != null && Debug.isOn("session")) {
|
|
1121 |
if (session != null) {
|
|
1122 |
System.out.println("%% Client cached "
|
|
1123 |
+ session
|
|
1124 |
+ (session.isRejoinable() ? "" : " (not rejoinable)"));
|
|
1125 |
} else {
|
|
1126 |
System.out.println("%% No cached client session");
|
|
1127 |
}
|
|
1128 |
}
|
|
1129 |
if ((session != null) && (session.isRejoinable() == false)) {
|
|
1130 |
session = null;
|
|
1131 |
}
|
|
1132 |
|
|
1133 |
if (session != null) {
|
|
1134 |
CipherSuite sessionSuite = session.getSuite();
|
|
1135 |
ProtocolVersion sessionVersion = session.getProtocolVersion();
|
6856
|
1136 |
if (isNegotiable(sessionSuite) == false) {
|
2
|
1137 |
if (debug != null && Debug.isOn("session")) {
|
6856
|
1138 |
System.out.println("%% can't resume, unavailable cipher");
|
2
|
1139 |
}
|
|
1140 |
session = null;
|
|
1141 |
}
|
|
1142 |
|
7039
|
1143 |
if ((session != null) && !isNegotiable(sessionVersion)) {
|
2
|
1144 |
if (debug != null && Debug.isOn("session")) {
|
|
1145 |
System.out.println("%% can't resume, protocol disabled");
|
|
1146 |
}
|
|
1147 |
session = null;
|
|
1148 |
}
|
|
1149 |
|
|
1150 |
if (session != null) {
|
|
1151 |
if (debug != null) {
|
|
1152 |
if (Debug.isOn("handshake") || Debug.isOn("session")) {
|
|
1153 |
System.out.println("%% Try resuming " + session
|
|
1154 |
+ " from port " + getLocalPortSE());
|
|
1155 |
}
|
|
1156 |
}
|
|
1157 |
|
6856
|
1158 |
sessionId = session.getSessionId();
|
2
|
1159 |
maxProtocolVersion = sessionVersion;
|
|
1160 |
|
|
1161 |
// Update SSL version number in underlying SSL socket and
|
|
1162 |
// handshake output stream, so that the output records (at the
|
|
1163 |
// record layer) have the correct version
|
|
1164 |
setVersion(sessionVersion);
|
|
1165 |
}
|
|
1166 |
|
6856
|
1167 |
/*
|
|
1168 |
* Force use of the previous session ciphersuite, and
|
|
1169 |
* add the SCSV if enabled.
|
|
1170 |
*/
|
2
|
1171 |
if (!enableNewSession) {
|
|
1172 |
if (session == null) {
|
7039
|
1173 |
throw new SSLHandshakeException(
|
2
|
1174 |
"Can't reuse existing SSL client session");
|
|
1175 |
}
|
6856
|
1176 |
|
7990
|
1177 |
Collection<CipherSuite> cipherList = new ArrayList<>(2);
|
6856
|
1178 |
cipherList.add(sessionSuite);
|
|
1179 |
if (!secureRenegotiation &&
|
|
1180 |
cipherSuites.contains(CipherSuite.C_SCSV)) {
|
|
1181 |
cipherList.add(CipherSuite.C_SCSV);
|
|
1182 |
} // otherwise, renegotiation_info extension will be used
|
|
1183 |
|
|
1184 |
cipherSuites = new CipherSuiteList(cipherList);
|
2
|
1185 |
}
|
|
1186 |
}
|
|
1187 |
|
6856
|
1188 |
if (session == null && !enableNewSession) {
|
7039
|
1189 |
throw new SSLHandshakeException("No existing session to resume");
|
6856
|
1190 |
}
|
|
1191 |
|
|
1192 |
// exclude SCSV for secure renegotiation
|
|
1193 |
if (secureRenegotiation && cipherSuites.contains(CipherSuite.C_SCSV)) {
|
|
1194 |
Collection<CipherSuite> cipherList =
|
7990
|
1195 |
new ArrayList<>(cipherSuites.size() - 1);
|
6856
|
1196 |
for (CipherSuite suite : cipherSuites.collection()) {
|
|
1197 |
if (suite != CipherSuite.C_SCSV) {
|
|
1198 |
cipherList.add(suite);
|
|
1199 |
}
|
|
1200 |
}
|
|
1201 |
|
|
1202 |
cipherSuites = new CipherSuiteList(cipherList);
|
|
1203 |
}
|
2
|
1204 |
|
6856
|
1205 |
// make sure there is a negotiable cipher suite.
|
|
1206 |
boolean negotiable = false;
|
|
1207 |
for (CipherSuite suite : cipherSuites.collection()) {
|
|
1208 |
if (isNegotiable(suite)) {
|
|
1209 |
negotiable = true;
|
|
1210 |
break;
|
|
1211 |
}
|
|
1212 |
}
|
|
1213 |
|
|
1214 |
if (!negotiable) {
|
7039
|
1215 |
throw new SSLHandshakeException("No negotiable cipher suite");
|
6856
|
1216 |
}
|
|
1217 |
|
7043
|
1218 |
// Not a TLS1.2+ handshake
|
|
1219 |
// For SSLv2Hello, HandshakeHash.reset() will be called, so we
|
|
1220 |
// cannot call HandshakeHash.protocolDetermined() here. As it does
|
|
1221 |
// not follow the spec that HandshakeHash.reset() can be only be
|
|
1222 |
// called before protocolDetermined.
|
|
1223 |
// if (maxProtocolVersion.v < ProtocolVersion.TLS12.v) {
|
7804
|
1224 |
// handshakeHash.protocolDetermined(maxProtocolVersion);
|
7043
|
1225 |
// }
|
|
1226 |
|
6856
|
1227 |
// create the ClientHello message
|
|
1228 |
ClientHello clientHelloMessage = new ClientHello(
|
|
1229 |
sslContext.getSecureRandom(), maxProtocolVersion,
|
|
1230 |
sessionId, cipherSuites);
|
|
1231 |
|
7043
|
1232 |
// add signature_algorithm extension
|
|
1233 |
if (maxProtocolVersion.v >= ProtocolVersion.TLS12.v) {
|
|
1234 |
// we will always send the signature_algorithm extension
|
|
1235 |
Collection<SignatureAndHashAlgorithm> localSignAlgs =
|
|
1236 |
getLocalSupportedSignAlgs();
|
|
1237 |
if (localSignAlgs.isEmpty()) {
|
|
1238 |
throw new SSLHandshakeException(
|
|
1239 |
"No supported signature algorithm");
|
|
1240 |
}
|
|
1241 |
|
|
1242 |
clientHelloMessage.addSignatureAlgorithmsExtension(localSignAlgs);
|
|
1243 |
}
|
|
1244 |
|
|
1245 |
// add server_name extension
|
|
1246 |
if (enableSNIExtension) {
|
|
1247 |
// We cannot use the hostname resolved from name services. For
|
|
1248 |
// virtual hosting, multiple hostnames may be bound to the same IP
|
|
1249 |
// address, so the hostname resolved from name services is not
|
|
1250 |
// reliable.
|
|
1251 |
String hostname = getRawHostnameSE();
|
|
1252 |
|
|
1253 |
// we only allow FQDN
|
|
1254 |
if (hostname != null && hostname.indexOf('.') > 0 &&
|
|
1255 |
!IPAddressUtil.isIPv4LiteralAddress(hostname) &&
|
|
1256 |
!IPAddressUtil.isIPv6LiteralAddress(hostname)) {
|
|
1257 |
clientHelloMessage.addServerNameIndicationExtension(hostname);
|
|
1258 |
}
|
|
1259 |
}
|
|
1260 |
|
6856
|
1261 |
// reset the client random cookie
|
|
1262 |
clnt_random = clientHelloMessage.clnt_random;
|
|
1263 |
|
|
1264 |
/*
|
|
1265 |
* need to set the renegotiation_info extension for:
|
|
1266 |
* 1: secure renegotiation
|
|
1267 |
* 2: initial handshake and no SCSV in the ClientHello
|
|
1268 |
* 3: insecure renegotiation and no SCSV in the ClientHello
|
|
1269 |
*/
|
|
1270 |
if (secureRenegotiation ||
|
|
1271 |
!cipherSuites.contains(CipherSuite.C_SCSV)) {
|
|
1272 |
clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData);
|
|
1273 |
}
|
|
1274 |
|
|
1275 |
return clientHelloMessage;
|
2
|
1276 |
}
|
|
1277 |
|
|
1278 |
/*
|
|
1279 |
* Fault detected during handshake.
|
|
1280 |
*/
|
|
1281 |
void handshakeAlert(byte description) throws SSLProtocolException {
|
|
1282 |
String message = Alerts.alertDescription(description);
|
|
1283 |
|
|
1284 |
if (debug != null && Debug.isOn("handshake")) {
|
|
1285 |
System.out.println("SSL - handshake alert: " + message);
|
|
1286 |
}
|
|
1287 |
throw new SSLProtocolException("handshake alert: " + message);
|
|
1288 |
}
|
|
1289 |
|
|
1290 |
/*
|
|
1291 |
* Unless we are using an anonymous ciphersuite, the server always
|
|
1292 |
* sends a certificate message (for the CipherSuites we currently
|
|
1293 |
* support). The trust manager verifies the chain for us.
|
|
1294 |
*/
|
|
1295 |
private void serverCertificate(CertificateMsg mesg) throws IOException {
|
|
1296 |
if (debug != null && Debug.isOn("handshake")) {
|
|
1297 |
mesg.print(System.out);
|
|
1298 |
}
|
|
1299 |
X509Certificate[] peerCerts = mesg.getCertificateChain();
|
|
1300 |
if (peerCerts.length == 0) {
|
|
1301 |
fatalSE(Alerts.alert_bad_certificate,
|
|
1302 |
"empty certificate chain");
|
|
1303 |
}
|
|
1304 |
// ask the trust manager to verify the chain
|
|
1305 |
X509TrustManager tm = sslContext.getX509TrustManager();
|
|
1306 |
try {
|
|
1307 |
// find out the key exchange algorithm used
|
|
1308 |
// use "RSA" for non-ephemeral "RSA_EXPORT"
|
|
1309 |
String keyExchangeString;
|
|
1310 |
if (keyExchange == K_RSA_EXPORT && !serverKeyExchangeReceived) {
|
|
1311 |
keyExchangeString = K_RSA.name;
|
|
1312 |
} else {
|
|
1313 |
keyExchangeString = keyExchange.name;
|
|
1314 |
}
|
|
1315 |
|
|
1316 |
if (tm instanceof X509ExtendedTrustManager) {
|
7043
|
1317 |
if (conn != null) {
|
|
1318 |
((X509ExtendedTrustManager)tm).checkServerTrusted(
|
|
1319 |
peerCerts.clone(),
|
2
|
1320 |
keyExchangeString,
|
7043
|
1321 |
conn);
|
|
1322 |
} else {
|
|
1323 |
((X509ExtendedTrustManager)tm).checkServerTrusted(
|
|
1324 |
peerCerts.clone(),
|
|
1325 |
keyExchangeString,
|
|
1326 |
engine);
|
|
1327 |
}
|
2
|
1328 |
} else {
|
7043
|
1329 |
// Unlikely to happen, because we have wrapped the old
|
|
1330 |
// X509TrustManager with the new X509ExtendedTrustManager.
|
|
1331 |
throw new CertificateException(
|
|
1332 |
"Improper X509TrustManager implementation");
|
2
|
1333 |
}
|
|
1334 |
} catch (CertificateException e) {
|
|
1335 |
// This will throw an exception, so include the original error.
|
|
1336 |
fatalSE(Alerts.alert_certificate_unknown, e);
|
|
1337 |
}
|
|
1338 |
session.setPeerCertificates(peerCerts);
|
|
1339 |
}
|
|
1340 |
}
|