|
1 /* |
|
2 * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any questions. |
|
24 */ |
|
25 |
|
26 |
|
27 package sun.security.ssl; |
|
28 |
|
29 import java.io.*; |
|
30 import java.util.*; |
|
31 import java.security.*; |
|
32 import java.security.cert.*; |
|
33 import java.security.interfaces.*; |
|
34 import java.security.spec.ECParameterSpec; |
|
35 |
|
36 import javax.crypto.SecretKey; |
|
37 import javax.crypto.spec.SecretKeySpec; |
|
38 |
|
39 import javax.net.ssl.*; |
|
40 |
|
41 import javax.security.auth.Subject; |
|
42 import javax.security.auth.kerberos.KerberosKey; |
|
43 import javax.security.auth.kerberos.KerberosPrincipal; |
|
44 import javax.security.auth.kerberos.ServicePermission; |
|
45 import sun.security.jgss.krb5.Krb5Util; |
|
46 import sun.security.jgss.GSSUtil; |
|
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.*; |
|
53 import static sun.security.ssl.CipherSuite.KeyExchange.*; |
|
54 |
|
55 /** |
|
56 * ServerHandshaker does the protocol handshaking from the point |
|
57 * of view of a server. It is driven asychronously by handshake messages |
|
58 * as delivered by the parent Handshaker class, and also uses |
|
59 * common functionality (e.g. key generation) that is provided there. |
|
60 * |
|
61 * @author David Brownell |
|
62 */ |
|
63 final class ServerHandshaker extends Handshaker { |
|
64 |
|
65 // is the server going to require the client to authenticate? |
|
66 private byte doClientAuth; |
|
67 |
|
68 // our authentication info |
|
69 private X509Certificate[] certs; |
|
70 private PrivateKey privateKey; |
|
71 |
|
72 private KerberosKey[] kerberosKeys; |
|
73 |
|
74 // flag to check for clientCertificateVerify message |
|
75 private boolean needClientVerify = false; |
|
76 |
|
77 /* |
|
78 * For exportable ciphersuites using non-exportable key sizes, we use |
|
79 * ephemeral RSA keys. We could also do anonymous RSA in the same way |
|
80 * but there are no such ciphersuites currently defined. |
|
81 */ |
|
82 private PrivateKey tempPrivateKey; |
|
83 private PublicKey tempPublicKey; |
|
84 |
|
85 /* |
|
86 * For anonymous and ephemeral Diffie-Hellman key exchange, we use |
|
87 * ephemeral Diffie-Hellman keys. |
|
88 */ |
|
89 private DHCrypt dh; |
|
90 |
|
91 // Helper for ECDH based key exchanges |
|
92 private ECDHCrypt ecdh; |
|
93 |
|
94 // version request by the client in its ClientHello |
|
95 // we remember it for the RSA premaster secret version check |
|
96 private ProtocolVersion clientRequestedVersion; |
|
97 |
|
98 private SupportedEllipticCurvesExtension supportedCurves; |
|
99 |
|
100 /* |
|
101 * Constructor ... use the keys found in the auth context. |
|
102 */ |
|
103 ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context, |
|
104 ProtocolList enabledProtocols, byte clientAuth) { |
|
105 super(socket, context, enabledProtocols, |
|
106 (clientAuth != SSLEngineImpl.clauth_none), false); |
|
107 doClientAuth = clientAuth; |
|
108 } |
|
109 |
|
110 /* |
|
111 * Constructor ... use the keys found in the auth context. |
|
112 */ |
|
113 ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context, |
|
114 ProtocolList enabledProtocols, byte clientAuth) { |
|
115 super(engine, context, enabledProtocols, |
|
116 (clientAuth != SSLEngineImpl.clauth_none), false); |
|
117 doClientAuth = clientAuth; |
|
118 } |
|
119 |
|
120 /* |
|
121 * As long as handshaking has not started, we can change |
|
122 * whether client authentication is required. Otherwise, |
|
123 * we will need to wait for the next handshake. |
|
124 */ |
|
125 void setClientAuth(byte clientAuth) { |
|
126 doClientAuth = clientAuth; |
|
127 } |
|
128 |
|
129 /* |
|
130 * This routine handles all the server side handshake messages, one at |
|
131 * a time. Given the message type (and in some cases the pending cipher |
|
132 * spec) it parses the type-specific message. Then it calls a function |
|
133 * that handles that specific message. |
|
134 * |
|
135 * It updates the state machine as each message is processed, and writes |
|
136 * responses as needed using the connection in the constructor. |
|
137 */ |
|
138 void processMessage(byte type, int message_len) |
|
139 throws IOException { |
|
140 // |
|
141 // In SSLv3 and TLS, messages follow strictly increasing |
|
142 // numerical order _except_ for one annoying special case. |
|
143 // |
|
144 if ((state > type) |
|
145 && (state != HandshakeMessage.ht_client_key_exchange |
|
146 && type != HandshakeMessage.ht_certificate_verify)) { |
|
147 throw new SSLProtocolException( |
|
148 "Handshake message sequence violation, state = " + state |
|
149 + ", type = " + type); |
|
150 } |
|
151 |
|
152 switch (type) { |
|
153 case HandshakeMessage.ht_client_hello: |
|
154 ClientHello ch = new ClientHello(input, message_len); |
|
155 /* |
|
156 * send it off for processing. |
|
157 */ |
|
158 this.clientHello(ch); |
|
159 break; |
|
160 |
|
161 case HandshakeMessage.ht_certificate: |
|
162 if (doClientAuth == SSLEngineImpl.clauth_none) { |
|
163 fatalSE(Alerts.alert_unexpected_message, |
|
164 "client sent unsolicited cert chain"); |
|
165 // NOTREACHED |
|
166 } |
|
167 this.clientCertificate(new CertificateMsg(input)); |
|
168 break; |
|
169 |
|
170 case HandshakeMessage.ht_client_key_exchange: |
|
171 SecretKey preMasterSecret; |
|
172 switch (keyExchange) { |
|
173 case K_RSA: |
|
174 case K_RSA_EXPORT: |
|
175 /* |
|
176 * The client's pre-master secret is decrypted using |
|
177 * either the server's normal private RSA key, or the |
|
178 * temporary one used for non-export or signing-only |
|
179 * certificates/keys. |
|
180 */ |
|
181 RSAClientKeyExchange pms = new RSAClientKeyExchange |
|
182 (protocolVersion, input, message_len, privateKey); |
|
183 preMasterSecret = this.clientKeyExchange(pms); |
|
184 break; |
|
185 case K_KRB5: |
|
186 case K_KRB5_EXPORT: |
|
187 preMasterSecret = this.clientKeyExchange( |
|
188 new KerberosClientKeyExchange(protocolVersion, |
|
189 clientRequestedVersion, |
|
190 sslContext.getSecureRandom(), |
|
191 input, |
|
192 kerberosKeys)); |
|
193 break; |
|
194 case K_DHE_RSA: |
|
195 case K_DHE_DSS: |
|
196 case K_DH_ANON: |
|
197 /* |
|
198 * The pre-master secret is derived using the normal |
|
199 * Diffie-Hellman calculation. Note that the main |
|
200 * protocol difference in these five flavors is in how |
|
201 * the ServerKeyExchange message was constructed! |
|
202 */ |
|
203 preMasterSecret = this.clientKeyExchange( |
|
204 new DHClientKeyExchange(input)); |
|
205 break; |
|
206 case K_ECDH_RSA: |
|
207 case K_ECDH_ECDSA: |
|
208 case K_ECDHE_RSA: |
|
209 case K_ECDHE_ECDSA: |
|
210 case K_ECDH_ANON: |
|
211 preMasterSecret = this.clientKeyExchange |
|
212 (new ECDHClientKeyExchange(input)); |
|
213 break; |
|
214 default: |
|
215 throw new SSLProtocolException |
|
216 ("Unrecognized key exchange: " + keyExchange); |
|
217 } |
|
218 |
|
219 // |
|
220 // All keys are calculated from the premaster secret |
|
221 // and the exchanged nonces in the same way. |
|
222 // |
|
223 calculateKeys(preMasterSecret, clientRequestedVersion); |
|
224 break; |
|
225 |
|
226 case HandshakeMessage.ht_certificate_verify: |
|
227 this.clientCertificateVerify(new CertificateVerify(input)); |
|
228 break; |
|
229 |
|
230 case HandshakeMessage.ht_finished: |
|
231 this.clientFinished(new Finished(protocolVersion, input)); |
|
232 break; |
|
233 |
|
234 default: |
|
235 throw new SSLProtocolException( |
|
236 "Illegal server handshake msg, " + type); |
|
237 } |
|
238 |
|
239 // |
|
240 // Move the state machine forward except for that annoying |
|
241 // special case. This means that clients could send extra |
|
242 // cert verify messages; not a problem so long as all of |
|
243 // them actually check out. |
|
244 // |
|
245 if (state < type && type != HandshakeMessage.ht_certificate_verify) { |
|
246 state = type; |
|
247 } |
|
248 } |
|
249 |
|
250 |
|
251 /* |
|
252 * ClientHello presents the server with a bunch of options, to which the |
|
253 * server replies with a ServerHello listing the ones which this session |
|
254 * will use. If needed, it also writes its Certificate plus in some cases |
|
255 * a ServerKeyExchange message. It may also write a CertificateRequest, |
|
256 * to elicit a client certificate. |
|
257 * |
|
258 * All these messages are terminated by a ServerHelloDone message. In |
|
259 * most cases, all this can be sent in a single Record. |
|
260 */ |
|
261 private void clientHello(ClientHello mesg) throws IOException { |
|
262 if (debug != null && Debug.isOn("handshake")) { |
|
263 mesg.print(System.out); |
|
264 } |
|
265 /* |
|
266 * Always make sure this entire record has been digested before we |
|
267 * start emitting output, to ensure correct digesting order. |
|
268 */ |
|
269 input.digestNow(); |
|
270 |
|
271 /* |
|
272 * FIRST, construct the ServerHello using the options and priorities |
|
273 * from the ClientHello. Update the (pending) cipher spec as we do |
|
274 * so, and save the client's version to protect against rollback |
|
275 * attacks. |
|
276 * |
|
277 * There are a bunch of minor tasks here, and one major one: deciding |
|
278 * if the short or the full handshake sequence will be used. |
|
279 */ |
|
280 ServerHello m1 = new ServerHello(); |
|
281 |
|
282 clientRequestedVersion = mesg.protocolVersion; |
|
283 |
|
284 // check if clientVersion is recent enough for us |
|
285 if (clientRequestedVersion.v < enabledProtocols.min.v) { |
|
286 fatalSE(Alerts.alert_handshake_failure, |
|
287 "Client requested protocol " + clientRequestedVersion + |
|
288 " not enabled or not supported"); |
|
289 } |
|
290 |
|
291 // now we know we have an acceptable version |
|
292 // use the lower of our max and the client requested version |
|
293 ProtocolVersion selectedVersion; |
|
294 if (clientRequestedVersion.v <= enabledProtocols.max.v) { |
|
295 selectedVersion = clientRequestedVersion; |
|
296 } else { |
|
297 selectedVersion = enabledProtocols.max; |
|
298 } |
|
299 setVersion(selectedVersion); |
|
300 |
|
301 m1.protocolVersion = protocolVersion; |
|
302 |
|
303 // |
|
304 // random ... save client and server values for later use |
|
305 // in computing the master secret (from pre-master secret) |
|
306 // and thence the other crypto keys. |
|
307 // |
|
308 // NOTE: this use of three inputs to generating _each_ set |
|
309 // of ciphers slows things down, but it does increase the |
|
310 // security since each connection in the session can hold |
|
311 // its own authenticated (and strong) keys. One could make |
|
312 // creation of a session a rare thing... |
|
313 // |
|
314 clnt_random = mesg.clnt_random; |
|
315 svr_random = new RandomCookie(sslContext.getSecureRandom()); |
|
316 m1.svr_random = svr_random; |
|
317 |
|
318 session = null; // forget about the current session |
|
319 // |
|
320 // Here we go down either of two paths: (a) the fast one, where |
|
321 // the client's asked to rejoin an existing session, and the server |
|
322 // permits this; (b) the other one, where a new session is created. |
|
323 // |
|
324 if (mesg.sessionId.length() != 0) { |
|
325 // client is trying to resume a session, let's see... |
|
326 |
|
327 SSLSessionImpl previous = ((SSLSessionContextImpl)sslContext |
|
328 .engineGetServerSessionContext()) |
|
329 .get(mesg.sessionId.getId()); |
|
330 // |
|
331 // Check if we can use the fast path, resuming a session. We |
|
332 // can do so iff we have a valid record for that session, and |
|
333 // the cipher suite for that session was on the list which the |
|
334 // client requested, and if we're not forgetting any needed |
|
335 // authentication on the part of the client. |
|
336 // |
|
337 if (previous != null) { |
|
338 resumingSession = previous.isRejoinable(); |
|
339 |
|
340 if (resumingSession) { |
|
341 ProtocolVersion oldVersion = previous.getProtocolVersion(); |
|
342 // cannot resume session with different version |
|
343 if (oldVersion != protocolVersion) { |
|
344 resumingSession = false; |
|
345 } |
|
346 } |
|
347 |
|
348 if (resumingSession && |
|
349 (doClientAuth == SSLEngineImpl.clauth_required)) { |
|
350 try { |
|
351 previous.getPeerPrincipal(); |
|
352 } catch (SSLPeerUnverifiedException e) { |
|
353 resumingSession = false; |
|
354 } |
|
355 } |
|
356 |
|
357 // validate subject identity |
|
358 if (resumingSession) { |
|
359 CipherSuite suite = previous.getSuite(); |
|
360 if (suite.keyExchange == K_KRB5 || |
|
361 suite.keyExchange == K_KRB5_EXPORT) { |
|
362 Principal localPrincipal = previous.getLocalPrincipal(); |
|
363 |
|
364 Subject subject = null; |
|
365 try { |
|
366 subject = AccessController.doPrivileged( |
|
367 new PrivilegedExceptionAction<Subject>() { |
|
368 public Subject run() throws Exception { |
|
369 return Krb5Util.getSubject( |
|
370 GSSUtil.CALLER_SSL_SERVER, |
|
371 getAccSE()); |
|
372 }}); |
|
373 } catch (PrivilegedActionException e) { |
|
374 subject = null; |
|
375 if (debug != null && Debug.isOn("session")) { |
|
376 System.out.println("Attempt to obtain" + |
|
377 " subject failed!"); |
|
378 } |
|
379 } |
|
380 |
|
381 if (subject != null) { |
|
382 Set<KerberosPrincipal> principals = |
|
383 subject.getPrincipals(KerberosPrincipal.class); |
|
384 if (!principals.contains(localPrincipal)) { |
|
385 resumingSession = false; |
|
386 if (debug != null && Debug.isOn("session")) { |
|
387 System.out.println("Subject identity" + |
|
388 " is not the same"); |
|
389 } |
|
390 } else { |
|
391 if (debug != null && Debug.isOn("session")) |
|
392 System.out.println("Subject identity" + |
|
393 " is same"); |
|
394 } |
|
395 } else { |
|
396 resumingSession = false; |
|
397 if (debug != null && Debug.isOn("session")) |
|
398 System.out.println("Kerberos credentials are" + |
|
399 " not present in the current Subject;" + |
|
400 " check if " + |
|
401 " javax.security.auth.useSubjectAsCreds" + |
|
402 " system property has been set to false"); |
|
403 } |
|
404 } |
|
405 } |
|
406 |
|
407 if (resumingSession) { |
|
408 CipherSuite suite = previous.getSuite(); |
|
409 // verify that the ciphersuite from the cached session |
|
410 // is in the list of client requested ciphersuites and |
|
411 // we have it enabled |
|
412 if ((isEnabled(suite) == false) || |
|
413 (mesg.getCipherSuites().contains(suite) == false)) { |
|
414 resumingSession = false; |
|
415 } else { |
|
416 // everything looks ok, set the ciphersuite |
|
417 // this should be done last when we are sure we |
|
418 // will resume |
|
419 setCipherSuite(suite); |
|
420 } |
|
421 } |
|
422 |
|
423 if (resumingSession) { |
|
424 session = previous; |
|
425 if (debug != null && |
|
426 (Debug.isOn("handshake") || Debug.isOn("session"))) { |
|
427 System.out.println("%% Resuming " + session); |
|
428 } |
|
429 } |
|
430 } |
|
431 } // else client did not try to resume |
|
432 |
|
433 // |
|
434 // If client hasn't specified a session we can resume, start a |
|
435 // new one and choose its cipher suite and compression options. |
|
436 // Unless new session creation is disabled for this connection! |
|
437 // |
|
438 if (session == null) { |
|
439 if (!enableNewSession) { |
|
440 throw new SSLException("Client did not resume a session"); |
|
441 } |
|
442 supportedCurves = (SupportedEllipticCurvesExtension)mesg.extensions.get |
|
443 (ExtensionType.EXT_ELLIPTIC_CURVES); |
|
444 chooseCipherSuite(mesg); |
|
445 session = new SSLSessionImpl(protocolVersion, cipherSuite, |
|
446 sslContext.getSecureRandom(), |
|
447 getHostAddressSE(), getPortSE()); |
|
448 session.setLocalPrivateKey(privateKey); |
|
449 // chooseCompression(mesg); |
|
450 } |
|
451 |
|
452 m1.cipherSuite = cipherSuite; |
|
453 m1.sessionId = session.getSessionId(); |
|
454 m1.compression_method = session.getCompression(); |
|
455 |
|
456 if (debug != null && Debug.isOn("handshake")) { |
|
457 m1.print(System.out); |
|
458 System.out.println("Cipher suite: " + session.getSuite()); |
|
459 } |
|
460 m1.write(output); |
|
461 |
|
462 // |
|
463 // If we are resuming a session, we finish writing handshake |
|
464 // messages right now and then finish. |
|
465 // |
|
466 if (resumingSession) { |
|
467 calculateConnectionKeys(session.getMasterSecret()); |
|
468 sendChangeCipherAndFinish(false); |
|
469 return; |
|
470 } |
|
471 |
|
472 |
|
473 /* |
|
474 * SECOND, write the server Certificate(s) if we need to. |
|
475 * |
|
476 * NOTE: while an "anonymous RSA" mode is explicitly allowed by |
|
477 * the protocol, we can't support it since all of the SSL flavors |
|
478 * defined in the protocol spec are explicitly stated to require |
|
479 * using RSA certificates. |
|
480 */ |
|
481 if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) { |
|
482 // Server certificates are omitted for Kerberos ciphers |
|
483 |
|
484 } else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) { |
|
485 if (certs == null) { |
|
486 throw new RuntimeException("no certificates"); |
|
487 } |
|
488 |
|
489 CertificateMsg m2 = new CertificateMsg(certs); |
|
490 |
|
491 /* |
|
492 * Set local certs in the SSLSession, output |
|
493 * debug info, and then actually write to the client. |
|
494 */ |
|
495 session.setLocalCertificates(certs); |
|
496 if (debug != null && Debug.isOn("handshake")) { |
|
497 m2.print(System.out); |
|
498 } |
|
499 m2.write(output); |
|
500 |
|
501 // XXX has some side effects with OS TCP buffering, |
|
502 // leave it out for now |
|
503 |
|
504 // let client verify chain in the meantime... |
|
505 // output.flush(); |
|
506 } else { |
|
507 if (certs != null) { |
|
508 throw new RuntimeException("anonymous keyexchange with certs"); |
|
509 } |
|
510 } |
|
511 |
|
512 /* |
|
513 * THIRD, the ServerKeyExchange message ... iff it's needed. |
|
514 * |
|
515 * It's usually needed unless there's an encryption-capable |
|
516 * RSA cert, or a D-H cert. The notable exception is that |
|
517 * exportable ciphers used with big RSA keys need to downgrade |
|
518 * to use short RSA keys, even when the key/cert encrypts OK. |
|
519 */ |
|
520 |
|
521 ServerKeyExchange m3; |
|
522 switch (keyExchange) { |
|
523 case K_RSA: |
|
524 case K_KRB5: |
|
525 case K_KRB5_EXPORT: |
|
526 // no server key exchange for RSA or KRB5 ciphersuites |
|
527 m3 = null; |
|
528 break; |
|
529 case K_RSA_EXPORT: |
|
530 if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) { |
|
531 try { |
|
532 m3 = new RSA_ServerKeyExchange( |
|
533 tempPublicKey, privateKey, |
|
534 clnt_random, svr_random, |
|
535 sslContext.getSecureRandom()); |
|
536 privateKey = tempPrivateKey; |
|
537 } catch (GeneralSecurityException e) { |
|
538 throwSSLException |
|
539 ("Error generating RSA server key exchange", e); |
|
540 m3 = null; // make compiler happy |
|
541 } |
|
542 } else { |
|
543 // RSA_EXPORT with short key, don't need ServerKeyExchange |
|
544 m3 = null; |
|
545 } |
|
546 break; |
|
547 case K_DHE_RSA: |
|
548 case K_DHE_DSS: |
|
549 try { |
|
550 m3 = new DH_ServerKeyExchange(dh, |
|
551 privateKey, |
|
552 clnt_random.random_bytes, |
|
553 svr_random.random_bytes, |
|
554 sslContext.getSecureRandom()); |
|
555 } catch (GeneralSecurityException e) { |
|
556 throwSSLException("Error generating DH server key exchange", e); |
|
557 m3 = null; // make compiler happy |
|
558 } |
|
559 break; |
|
560 case K_DH_ANON: |
|
561 m3 = new DH_ServerKeyExchange(dh); |
|
562 break; |
|
563 case K_ECDHE_RSA: |
|
564 case K_ECDHE_ECDSA: |
|
565 case K_ECDH_ANON: |
|
566 try { |
|
567 m3 = new ECDH_ServerKeyExchange(ecdh, |
|
568 privateKey, |
|
569 clnt_random.random_bytes, |
|
570 svr_random.random_bytes, |
|
571 sslContext.getSecureRandom()); |
|
572 } catch (GeneralSecurityException e) { |
|
573 throwSSLException("Error generating ECDH server key exchange", e); |
|
574 m3 = null; // make compiler happy |
|
575 } |
|
576 break; |
|
577 case K_ECDH_RSA: |
|
578 case K_ECDH_ECDSA: |
|
579 // ServerKeyExchange not used for fixed ECDH |
|
580 m3 = null; |
|
581 break; |
|
582 default: |
|
583 throw new RuntimeException("internal error: " + keyExchange); |
|
584 } |
|
585 if (m3 != null) { |
|
586 if (debug != null && Debug.isOn("handshake")) { |
|
587 m3.print(System.out); |
|
588 } |
|
589 m3.write(output); |
|
590 } |
|
591 |
|
592 // |
|
593 // FOURTH, the CertificateRequest message. The details of |
|
594 // the message can be affected by the key exchange algorithm |
|
595 // in use. For example, certs with fixed Diffie-Hellman keys |
|
596 // are only useful with the DH_DSS and DH_RSA key exchange |
|
597 // algorithms. |
|
598 // |
|
599 // Needed only if server requires client to authenticate self. |
|
600 // Illegal for anonymous flavors, so we need to check that. |
|
601 // |
|
602 if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) { |
|
603 // CertificateRequest is omitted for Kerberos ciphers |
|
604 |
|
605 } else if (doClientAuth != SSLEngineImpl.clauth_none && |
|
606 keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON) { |
|
607 CertificateRequest m4; |
|
608 X509Certificate caCerts[]; |
|
609 |
|
610 caCerts = sslContext.getX509TrustManager().getAcceptedIssuers(); |
|
611 m4 = new CertificateRequest(caCerts, keyExchange); |
|
612 |
|
613 if (debug != null && Debug.isOn("handshake")) { |
|
614 m4.print(System.out); |
|
615 } |
|
616 m4.write(output); |
|
617 } |
|
618 |
|
619 /* |
|
620 * FIFTH, say ServerHelloDone. |
|
621 */ |
|
622 ServerHelloDone m5 = new ServerHelloDone(); |
|
623 |
|
624 if (debug != null && Debug.isOn("handshake")) { |
|
625 m5.print(System.out); |
|
626 } |
|
627 m5.write(output); |
|
628 |
|
629 /* |
|
630 * Flush any buffered messages so the client will see them. |
|
631 * Ideally, all the messages above go in a single network level |
|
632 * message to the client. Without big Certificate chains, it's |
|
633 * going to be the common case. |
|
634 */ |
|
635 output.flush(); |
|
636 } |
|
637 |
|
638 /* |
|
639 * Choose cipher suite from among those supported by client. Sets |
|
640 * the cipherSuite and keyExchange variables. |
|
641 */ |
|
642 private void chooseCipherSuite(ClientHello mesg) throws IOException { |
|
643 for (CipherSuite suite : mesg.getCipherSuites().collection()) { |
|
644 if (isEnabled(suite) == false) { |
|
645 continue; |
|
646 } |
|
647 if (doClientAuth == SSLEngineImpl.clauth_required) { |
|
648 if ((suite.keyExchange == K_DH_ANON) || (suite.keyExchange == K_ECDH_ANON)) { |
|
649 continue; |
|
650 } |
|
651 } |
|
652 if (trySetCipherSuite(suite) == false) { |
|
653 continue; |
|
654 } |
|
655 return; |
|
656 } |
|
657 fatalSE(Alerts.alert_handshake_failure, |
|
658 "no cipher suites in common"); |
|
659 } |
|
660 |
|
661 /** |
|
662 * Set the given CipherSuite, if possible. Return the result. |
|
663 * The call succeeds if the CipherSuite is available and we have |
|
664 * the necessary certificates to complete the handshake. We don't |
|
665 * check if the CipherSuite is actually enabled. |
|
666 * |
|
667 * If successful, this method also generates ephemeral keys if |
|
668 * required for this ciphersuite. This may take some time, so this |
|
669 * method should only be called if you really want to use the |
|
670 * CipherSuite. |
|
671 * |
|
672 * This method is called from chooseCipherSuite() in this class |
|
673 * and SSLServerSocketImpl.checkEnabledSuites() (as a sanity check). |
|
674 */ |
|
675 boolean trySetCipherSuite(CipherSuite suite) { |
|
676 /* |
|
677 * If we're resuming a session we know we can |
|
678 * support this key exchange algorithm and in fact |
|
679 * have already cached the result of it in |
|
680 * the session state. |
|
681 */ |
|
682 if (resumingSession) { |
|
683 return true; |
|
684 } |
|
685 |
|
686 if (suite.isAvailable() == false) { |
|
687 return false; |
|
688 } |
|
689 |
|
690 KeyExchange keyExchange = suite.keyExchange; |
|
691 |
|
692 // null out any existing references |
|
693 privateKey = null; |
|
694 certs = null; |
|
695 dh = null; |
|
696 tempPrivateKey = null; |
|
697 tempPublicKey = null; |
|
698 |
|
699 switch (keyExchange) { |
|
700 case K_RSA: |
|
701 case K_RSA_EXPORT: |
|
702 case K_DHE_RSA: |
|
703 case K_ECDHE_RSA: |
|
704 // need RSA certs for authentication |
|
705 if (setupPrivateKeyAndChain("RSA") == false) { |
|
706 return false; |
|
707 } |
|
708 |
|
709 if (keyExchange == K_RSA_EXPORT) { |
|
710 try { |
|
711 if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) { |
|
712 if (!setupEphemeralRSAKeys(suite.exportable)) { |
|
713 return false; |
|
714 } |
|
715 } |
|
716 } catch (RuntimeException e) { |
|
717 // could not determine keylength, ignore key |
|
718 return false; |
|
719 } |
|
720 } else if (keyExchange == K_DHE_RSA) { |
|
721 setupEphemeralDHKeys(suite.exportable); |
|
722 } else if (keyExchange == K_ECDHE_RSA) { |
|
723 if (setupEphemeralECDHKeys() == false) { |
|
724 return false; |
|
725 } |
|
726 } // else nothing more to do for K_RSA |
|
727 break; |
|
728 case K_DHE_DSS: |
|
729 // need DSS certs for authentication |
|
730 if (setupPrivateKeyAndChain("DSA") == false) { |
|
731 return false; |
|
732 } |
|
733 setupEphemeralDHKeys(suite.exportable); |
|
734 break; |
|
735 case K_ECDHE_ECDSA: |
|
736 // need EC cert signed using EC |
|
737 if (setupPrivateKeyAndChain("EC_EC") == false) { |
|
738 return false; |
|
739 } |
|
740 if (setupEphemeralECDHKeys() == false) { |
|
741 return false; |
|
742 } |
|
743 break; |
|
744 case K_ECDH_RSA: |
|
745 // need EC cert signed using RSA |
|
746 if (setupPrivateKeyAndChain("EC_RSA") == false) { |
|
747 return false; |
|
748 } |
|
749 setupStaticECDHKeys(); |
|
750 break; |
|
751 case K_ECDH_ECDSA: |
|
752 // need EC cert signed using EC |
|
753 if (setupPrivateKeyAndChain("EC_EC") == false) { |
|
754 return false; |
|
755 } |
|
756 setupStaticECDHKeys(); |
|
757 break; |
|
758 case K_KRB5: |
|
759 case K_KRB5_EXPORT: |
|
760 // need Kerberos Key |
|
761 if (!setupKerberosKeys()) { |
|
762 return false; |
|
763 } |
|
764 break; |
|
765 case K_DH_ANON: |
|
766 // no certs needed for anonymous |
|
767 setupEphemeralDHKeys(suite.exportable); |
|
768 break; |
|
769 case K_ECDH_ANON: |
|
770 // no certs needed for anonymous |
|
771 if (setupEphemeralECDHKeys() == false) { |
|
772 return false; |
|
773 } |
|
774 break; |
|
775 default: |
|
776 // internal error, unknown key exchange |
|
777 throw new RuntimeException("Unrecognized cipherSuite: " + suite); |
|
778 } |
|
779 setCipherSuite(suite); |
|
780 return true; |
|
781 } |
|
782 |
|
783 /* |
|
784 * Get some "ephemeral" RSA keys for this context. This means |
|
785 * generating them if it's not already been done. |
|
786 * |
|
787 * Note that we currently do not implement any ciphersuites that use |
|
788 * strong ephemeral RSA. (We do not support the EXPORT1024 ciphersuites |
|
789 * and standard RSA ciphersuites prohibit ephemeral mode for some reason) |
|
790 * This means that export is always true and 512 bit keys are generated. |
|
791 */ |
|
792 private boolean setupEphemeralRSAKeys(boolean export) { |
|
793 KeyPair kp = sslContext.getEphemeralKeyManager(). |
|
794 getRSAKeyPair(export, sslContext.getSecureRandom()); |
|
795 if (kp == null) { |
|
796 return false; |
|
797 } else { |
|
798 tempPublicKey = kp.getPublic(); |
|
799 tempPrivateKey = kp.getPrivate(); |
|
800 return true; |
|
801 } |
|
802 } |
|
803 |
|
804 /* |
|
805 * Acquire some "ephemeral" Diffie-Hellman keys for this handshake. |
|
806 * We don't reuse these, for improved forward secrecy. |
|
807 */ |
|
808 private void setupEphemeralDHKeys(boolean export) { |
|
809 /* |
|
810 * Diffie-Hellman keys ... we use 768 bit private keys due |
|
811 * to the "use twice as many key bits as bits you want secret" |
|
812 * rule of thumb, assuming we want the same size premaster |
|
813 * secret with Diffie-Hellman and RSA key exchanges. Except |
|
814 * that exportable ciphers max out at 512 bits modulus values. |
|
815 */ |
|
816 dh = new DHCrypt((export ? 512 : 768), sslContext.getSecureRandom()); |
|
817 } |
|
818 |
|
819 // Setup the ephemeral ECDH parameters. |
|
820 // If we cannot continue because we do not support any of the curves that |
|
821 // the client requested, return false. Otherwise (all is well), return true. |
|
822 private boolean setupEphemeralECDHKeys() { |
|
823 int index = -1; |
|
824 if (supportedCurves != null) { |
|
825 // if the client sent the supported curves extension, pick the |
|
826 // first one that we support; |
|
827 for (int curveId : supportedCurves.curveIds()) { |
|
828 if (SupportedEllipticCurvesExtension.isSupported(curveId)) { |
|
829 index = curveId; |
|
830 break; |
|
831 } |
|
832 } |
|
833 if (index < 0) { |
|
834 // no match found, cannot use this ciphersuite |
|
835 return false; |
|
836 } |
|
837 } else { |
|
838 // pick our preference |
|
839 index = SupportedEllipticCurvesExtension.DEFAULT.curveIds()[0]; |
|
840 } |
|
841 String oid = SupportedEllipticCurvesExtension.getCurveOid(index); |
|
842 ecdh = new ECDHCrypt(oid, sslContext.getSecureRandom()); |
|
843 return true; |
|
844 } |
|
845 |
|
846 private void setupStaticECDHKeys() { |
|
847 // don't need to check whether the curve is supported, already done |
|
848 // in setupPrivateKeyAndChain(). |
|
849 ecdh = new ECDHCrypt(privateKey, certs[0].getPublicKey()); |
|
850 } |
|
851 |
|
852 /** |
|
853 * Retrieve the server key and certificate for the specified algorithm |
|
854 * from the KeyManager and set the instance variables. |
|
855 * |
|
856 * @return true if successful, false if not available or invalid |
|
857 */ |
|
858 private boolean setupPrivateKeyAndChain(String algorithm) { |
|
859 X509ExtendedKeyManager km = sslContext.getX509KeyManager(); |
|
860 String alias; |
|
861 if (conn != null) { |
|
862 alias = km.chooseServerAlias(algorithm, null, conn); |
|
863 } else { |
|
864 alias = km.chooseEngineServerAlias(algorithm, null, engine); |
|
865 } |
|
866 if (alias == null) { |
|
867 return false; |
|
868 } |
|
869 PrivateKey tempPrivateKey = km.getPrivateKey(alias); |
|
870 if (tempPrivateKey == null) { |
|
871 return false; |
|
872 } |
|
873 X509Certificate[] tempCerts = km.getCertificateChain(alias); |
|
874 if ((tempCerts == null) || (tempCerts.length == 0)) { |
|
875 return false; |
|
876 } |
|
877 String keyAlgorithm = algorithm.split("_")[0]; |
|
878 PublicKey publicKey = tempCerts[0].getPublicKey(); |
|
879 if ((tempPrivateKey.getAlgorithm().equals(keyAlgorithm) == false) |
|
880 || (publicKey.getAlgorithm().equals(keyAlgorithm) == false)) { |
|
881 return false; |
|
882 } |
|
883 // For ECC certs, check whether we support the EC domain parameters. |
|
884 // If the client sent a SupportedEllipticCurves ClientHello extension, |
|
885 // check against that too. |
|
886 if (keyAlgorithm.equals("EC")) { |
|
887 if (publicKey instanceof ECPublicKey == false) { |
|
888 return false; |
|
889 } |
|
890 ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); |
|
891 int index = SupportedEllipticCurvesExtension.getCurveIndex(params); |
|
892 if (SupportedEllipticCurvesExtension.isSupported(index) == false) { |
|
893 return false; |
|
894 } |
|
895 if ((supportedCurves != null) && !supportedCurves.contains(index)) { |
|
896 return false; |
|
897 } |
|
898 } |
|
899 this.privateKey = tempPrivateKey; |
|
900 this.certs = tempCerts; |
|
901 return true; |
|
902 } |
|
903 |
|
904 /** |
|
905 * Retrieve the Kerberos key for the specified server principal |
|
906 * from the JAAS configuration file. |
|
907 * |
|
908 * @return true if successful, false if not available or invalid |
|
909 */ |
|
910 private boolean setupKerberosKeys() { |
|
911 if (kerberosKeys != null) { |
|
912 return true; |
|
913 } |
|
914 try { |
|
915 final AccessControlContext acc = getAccSE(); |
|
916 kerberosKeys = AccessController.doPrivileged( |
|
917 new PrivilegedExceptionAction<KerberosKey[]>() { |
|
918 public KerberosKey[] run() throws Exception { |
|
919 // get kerberos key for the default principal |
|
920 return Krb5Util.getKeys( |
|
921 GSSUtil.CALLER_SSL_SERVER, null, acc); |
|
922 }}); |
|
923 |
|
924 // check permission to access and use the secret key of the |
|
925 // Kerberized "host" service |
|
926 if (kerberosKeys != null) { |
|
927 |
|
928 if (debug != null && Debug.isOn("handshake")) { |
|
929 System.out.println("Using Kerberos key: " + |
|
930 kerberosKeys[0]); |
|
931 } |
|
932 |
|
933 String serverPrincipal = |
|
934 kerberosKeys[0].getPrincipal().getName(); |
|
935 SecurityManager sm = System.getSecurityManager(); |
|
936 try { |
|
937 if (sm != null) { |
|
938 sm.checkPermission(new ServicePermission(serverPrincipal, |
|
939 "accept"), acc); |
|
940 } |
|
941 } catch (SecurityException se) { |
|
942 kerberosKeys = null; |
|
943 // %%% destroy keys? or will that affect Subject? |
|
944 if (debug != null && Debug.isOn("handshake")) |
|
945 System.out.println("Permission to access Kerberos" |
|
946 + " secret key denied"); |
|
947 return false; |
|
948 } |
|
949 } |
|
950 return (kerberosKeys != null); |
|
951 } catch (PrivilegedActionException e) { |
|
952 // Likely exception here is LoginExceptin |
|
953 if (debug != null && Debug.isOn("handshake")) { |
|
954 System.out.println("Attempt to obtain Kerberos key failed: " |
|
955 + e.toString()); |
|
956 } |
|
957 return false; |
|
958 } |
|
959 } |
|
960 |
|
961 /* |
|
962 * For Kerberos ciphers, the premaster secret is encrypted using |
|
963 * the session key. See RFC 2712. |
|
964 */ |
|
965 private SecretKey clientKeyExchange(KerberosClientKeyExchange mesg) |
|
966 throws IOException { |
|
967 |
|
968 if (debug != null && Debug.isOn("handshake")) { |
|
969 mesg.print(System.out); |
|
970 } |
|
971 |
|
972 // Record the principals involved in exchange |
|
973 session.setPeerPrincipal(mesg.getPeerPrincipal()); |
|
974 session.setLocalPrincipal(mesg.getLocalPrincipal()); |
|
975 |
|
976 byte[] b = mesg.getPreMasterSecret().getUnencrypted(); |
|
977 return new SecretKeySpec(b, "TlsPremasterSecret"); |
|
978 } |
|
979 |
|
980 /* |
|
981 * Diffie Hellman key exchange is used when the server presented |
|
982 * D-H parameters in its certificate (signed using RSA or DSS/DSA), |
|
983 * or else the server presented no certificate but sent D-H params |
|
984 * in a ServerKeyExchange message. Use of D-H is specified by the |
|
985 * cipher suite chosen. |
|
986 * |
|
987 * The message optionally contains the client's D-H public key (if |
|
988 * it wasn't not sent in a client certificate). As always with D-H, |
|
989 * if a client and a server have each other's D-H public keys and |
|
990 * they use common algorithm parameters, they have a shared key |
|
991 * that's derived via the D-H calculation. That key becomes the |
|
992 * pre-master secret. |
|
993 */ |
|
994 private SecretKey clientKeyExchange(DHClientKeyExchange mesg) |
|
995 throws IOException { |
|
996 |
|
997 if (debug != null && Debug.isOn("handshake")) { |
|
998 mesg.print(System.out); |
|
999 } |
|
1000 return dh.getAgreedSecret(mesg.getClientPublicKey()); |
|
1001 } |
|
1002 |
|
1003 private SecretKey clientKeyExchange(ECDHClientKeyExchange mesg) |
|
1004 throws IOException { |
|
1005 |
|
1006 if (debug != null && Debug.isOn("handshake")) { |
|
1007 mesg.print(System.out); |
|
1008 } |
|
1009 return ecdh.getAgreedSecret(mesg.getEncodedPoint()); |
|
1010 } |
|
1011 |
|
1012 /* |
|
1013 * Client wrote a message to verify the certificate it sent earlier. |
|
1014 * |
|
1015 * Note that this certificate isn't involved in key exchange. Client |
|
1016 * authentication messages are included in the checksums used to |
|
1017 * validate the handshake (e.g. Finished messages). Other than that, |
|
1018 * the _exact_ identity of the client is less fundamental to protocol |
|
1019 * security than its role in selecting keys via the pre-master secret. |
|
1020 */ |
|
1021 private void clientCertificateVerify(CertificateVerify mesg) |
|
1022 throws IOException { |
|
1023 |
|
1024 if (debug != null && Debug.isOn("handshake")) { |
|
1025 mesg.print(System.out); |
|
1026 } |
|
1027 |
|
1028 try { |
|
1029 PublicKey publicKey = |
|
1030 session.getPeerCertificates()[0].getPublicKey(); |
|
1031 |
|
1032 boolean valid = mesg.verify(protocolVersion, handshakeHash, |
|
1033 publicKey, session.getMasterSecret()); |
|
1034 if (valid == false) { |
|
1035 fatalSE(Alerts.alert_bad_certificate, |
|
1036 "certificate verify message signature error"); |
|
1037 } |
|
1038 } catch (GeneralSecurityException e) { |
|
1039 fatalSE(Alerts.alert_bad_certificate, |
|
1040 "certificate verify format error", e); |
|
1041 } |
|
1042 |
|
1043 // reset the flag for clientCertificateVerify message |
|
1044 needClientVerify = false; |
|
1045 } |
|
1046 |
|
1047 |
|
1048 /* |
|
1049 * Client writes "finished" at the end of its handshake, after cipher |
|
1050 * spec is changed. We verify it and then send ours. |
|
1051 * |
|
1052 * When we're resuming a session, we'll have already sent our own |
|
1053 * Finished message so just the verification is needed. |
|
1054 */ |
|
1055 private void clientFinished(Finished mesg) throws IOException { |
|
1056 if (debug != null && Debug.isOn("handshake")) { |
|
1057 mesg.print(System.out); |
|
1058 } |
|
1059 |
|
1060 /* |
|
1061 * Verify if client did send the certificate when client |
|
1062 * authentication was required, otherwise server should not proceed |
|
1063 */ |
|
1064 if (doClientAuth == SSLEngineImpl.clauth_required) { |
|
1065 // get X500Principal of the end-entity certificate for X509-based |
|
1066 // ciphersuites, or Kerberos principal for Kerberos ciphersuites |
|
1067 session.getPeerPrincipal(); |
|
1068 } |
|
1069 |
|
1070 /* |
|
1071 * Verify if client did send clientCertificateVerify message following |
|
1072 * the client Certificate, otherwise server should not proceed |
|
1073 */ |
|
1074 if (needClientVerify) { |
|
1075 fatalSE(Alerts.alert_handshake_failure, |
|
1076 "client did not send certificate verify message"); |
|
1077 } |
|
1078 |
|
1079 /* |
|
1080 * Verify the client's message with the "before" digest of messages, |
|
1081 * and forget about continuing to use that digest. |
|
1082 */ |
|
1083 boolean verified = mesg.verify(protocolVersion, handshakeHash, |
|
1084 Finished.CLIENT, session.getMasterSecret()); |
|
1085 |
|
1086 if (!verified) { |
|
1087 fatalSE(Alerts.alert_handshake_failure, |
|
1088 "client 'finished' message doesn't verify"); |
|
1089 // NOTREACHED |
|
1090 } |
|
1091 |
|
1092 /* |
|
1093 * OK, it verified. If we're doing the full handshake, add that |
|
1094 * "Finished" message to the hash of handshake messages, then send |
|
1095 * the change_cipher_spec and Finished message. |
|
1096 */ |
|
1097 if (!resumingSession) { |
|
1098 input.digestNow(); |
|
1099 sendChangeCipherAndFinish(true); |
|
1100 } |
|
1101 |
|
1102 /* |
|
1103 * Update the session cache only after the handshake completed, else |
|
1104 * we're open to an attack against a partially completed handshake. |
|
1105 */ |
|
1106 session.setLastAccessedTime(System.currentTimeMillis()); |
|
1107 if (!resumingSession && session.isRejoinable()) { |
|
1108 ((SSLSessionContextImpl)sslContext.engineGetServerSessionContext()) |
|
1109 .put(session); |
|
1110 if (debug != null && Debug.isOn("session")) { |
|
1111 System.out.println( |
|
1112 "%% Cached server session: " + session); |
|
1113 } |
|
1114 } else if (!resumingSession && |
|
1115 debug != null && Debug.isOn("session")) { |
|
1116 System.out.println( |
|
1117 "%% Didn't cache non-resumable server session: " |
|
1118 + session); |
|
1119 } |
|
1120 } |
|
1121 |
|
1122 /* |
|
1123 * Compute finished message with the "server" digest (and then forget |
|
1124 * about that digest, it can't be used again). |
|
1125 */ |
|
1126 private void sendChangeCipherAndFinish(boolean finishedTag) |
|
1127 throws IOException { |
|
1128 |
|
1129 output.flush(); |
|
1130 |
|
1131 Finished mesg = new Finished(protocolVersion, handshakeHash, |
|
1132 Finished.SERVER, session.getMasterSecret()); |
|
1133 |
|
1134 /* |
|
1135 * Send the change_cipher_spec record; then our Finished handshake |
|
1136 * message will be the last handshake message. Flush, and now we |
|
1137 * are ready for application data!! |
|
1138 */ |
|
1139 sendChangeCipherSpec(mesg, finishedTag); |
|
1140 |
|
1141 /* |
|
1142 * Update state machine so client MUST send 'finished' next |
|
1143 * The update should only take place if it is not in the fast |
|
1144 * handshake mode since the server has to wait for a finished |
|
1145 * message from the client. |
|
1146 */ |
|
1147 if (finishedTag) { |
|
1148 state = HandshakeMessage.ht_finished; |
|
1149 } |
|
1150 } |
|
1151 |
|
1152 |
|
1153 /* |
|
1154 * Returns a HelloRequest message to kickstart renegotiations |
|
1155 */ |
|
1156 HandshakeMessage getKickstartMessage() { |
|
1157 return new HelloRequest(); |
|
1158 } |
|
1159 |
|
1160 |
|
1161 /* |
|
1162 * Fault detected during handshake. |
|
1163 */ |
|
1164 void handshakeAlert(byte description) throws SSLProtocolException { |
|
1165 |
|
1166 String message = Alerts.alertDescription(description); |
|
1167 |
|
1168 if (debug != null && Debug.isOn("handshake")) { |
|
1169 System.out.println("SSL -- handshake alert: " |
|
1170 + message); |
|
1171 } |
|
1172 |
|
1173 /* |
|
1174 * It's ok to get a no_certificate alert from a client of which |
|
1175 * we *requested* authentication information. |
|
1176 * However, if we *required* it, then this is not acceptable. |
|
1177 * |
|
1178 * Anyone calling getPeerCertificates() on the |
|
1179 * session will get an SSLPeerUnverifiedException. |
|
1180 */ |
|
1181 if ((description == Alerts.alert_no_certificate) && |
|
1182 (doClientAuth == SSLEngineImpl.clauth_requested)) { |
|
1183 return; |
|
1184 } |
|
1185 |
|
1186 throw new SSLProtocolException("handshake alert: " + message); |
|
1187 } |
|
1188 |
|
1189 /* |
|
1190 * RSA key exchange is normally used. The client encrypts a "pre-master |
|
1191 * secret" with the server's public key, from the Certificate (or else |
|
1192 * ServerKeyExchange) message that was sent to it by the server. That's |
|
1193 * decrypted using the private key before we get here. |
|
1194 */ |
|
1195 private SecretKey clientKeyExchange(RSAClientKeyExchange mesg) throws IOException { |
|
1196 |
|
1197 if (debug != null && Debug.isOn("handshake")) { |
|
1198 mesg.print(System.out); |
|
1199 } |
|
1200 return mesg.preMaster; |
|
1201 } |
|
1202 |
|
1203 /* |
|
1204 * Verify the certificate sent by the client. We'll only get one if we |
|
1205 * sent a CertificateRequest to request client authentication. If we |
|
1206 * are in TLS mode, the client may send a message with no certificates |
|
1207 * to indicate it does not have an appropriate chain. (In SSLv3 mode, |
|
1208 * it would send a no certificate alert). |
|
1209 */ |
|
1210 private void clientCertificate(CertificateMsg mesg) throws IOException { |
|
1211 if (debug != null && Debug.isOn("handshake")) { |
|
1212 mesg.print(System.out); |
|
1213 } |
|
1214 |
|
1215 X509Certificate[] peerCerts = mesg.getCertificateChain(); |
|
1216 |
|
1217 if (peerCerts.length == 0) { |
|
1218 /* |
|
1219 * If the client authentication is only *REQUESTED* (e.g. |
|
1220 * not *REQUIRED*, this is an acceptable condition.) |
|
1221 */ |
|
1222 if (doClientAuth == SSLEngineImpl.clauth_requested) { |
|
1223 return; |
|
1224 } else { |
|
1225 fatalSE(Alerts.alert_bad_certificate, |
|
1226 "null cert chain"); |
|
1227 } |
|
1228 } |
|
1229 |
|
1230 // ask the trust manager to verify the chain |
|
1231 X509TrustManager tm = sslContext.getX509TrustManager(); |
|
1232 |
|
1233 try { |
|
1234 // find out the types of client authentication used |
|
1235 PublicKey key = peerCerts[0].getPublicKey(); |
|
1236 String keyAlgorithm = key.getAlgorithm(); |
|
1237 String authType; |
|
1238 if (keyAlgorithm.equals("RSA")) { |
|
1239 authType = "RSA"; |
|
1240 } else if (keyAlgorithm.equals("DSA")) { |
|
1241 authType = "DSA"; |
|
1242 } else if (keyAlgorithm.equals("EC")) { |
|
1243 authType = "EC"; |
|
1244 } else { |
|
1245 // unknown public key type |
|
1246 authType = "UNKNOWN"; |
|
1247 } |
|
1248 |
|
1249 String identificator = getHostnameVerificationSE(); |
|
1250 if (tm instanceof X509ExtendedTrustManager) { |
|
1251 ((X509ExtendedTrustManager)tm).checkClientTrusted( |
|
1252 (peerCerts != null ? |
|
1253 peerCerts.clone() : |
|
1254 null), |
|
1255 authType, |
|
1256 getHostSE(), |
|
1257 identificator); |
|
1258 } else { |
|
1259 if (identificator != null) { |
|
1260 throw new RuntimeException( |
|
1261 "trust manager does not support peer identification"); |
|
1262 } |
|
1263 |
|
1264 tm.checkClientTrusted( |
|
1265 (peerCerts != null ? |
|
1266 peerCerts.clone() : |
|
1267 peerCerts), |
|
1268 authType); |
|
1269 } |
|
1270 } catch (CertificateException e) { |
|
1271 // This will throw an exception, so include the original error. |
|
1272 fatalSE(Alerts.alert_certificate_unknown, e); |
|
1273 } |
|
1274 // set the flag for clientCertificateVerify message |
|
1275 needClientVerify = true; |
|
1276 |
|
1277 session.setPeerCertificates(peerCerts); |
|
1278 } |
|
1279 } |