1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.conscrypt; 19 20 import java.io.IOException; 21 import java.security.Key; 22 import java.security.KeyFactory; 23 import java.security.KeyPair; 24 import java.security.KeyPairGenerator; 25 import java.security.NoSuchAlgorithmException; 26 import java.security.PrivateKey; 27 import java.security.PublicKey; 28 import java.security.cert.CertificateException; 29 import java.security.cert.X509Certificate; 30 import java.util.Arrays; 31 import javax.crypto.Cipher; 32 import javax.crypto.KeyAgreement; 33 import javax.crypto.interfaces.DHKey; 34 import javax.crypto.interfaces.DHPublicKey; 35 import javax.crypto.spec.DHParameterSpec; 36 import javax.crypto.spec.DHPublicKeySpec; 37 import javax.crypto.spec.SecretKeySpec; 38 import javax.net.ssl.X509ExtendedKeyManager; 39 import javax.net.ssl.X509KeyManager; 40 import javax.net.ssl.X509TrustManager; 41 import javax.security.auth.x500.X500Principal; 42 43 /** 44 * Client side handshake protocol implementation. 45 * Handshake protocol operates on top of the Record Protocol. 46 * It is responsible for session negotiating. 47 * 48 * The implementation processes inbound server handshake messages, 49 * creates and sends respond messages. Outbound messages are supplied 50 * to Record Protocol. Detected errors are reported to the Alert protocol. 51 * 52 * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7. The 53 * TLS Handshake Protocol</a> 54 * 55 */ 56 public class ClientHandshakeImpl extends HandshakeProtocol { 57 58 /** 59 * Creates Client Handshake Implementation 60 * 61 * @param owner 62 */ 63 ClientHandshakeImpl(Object owner) { 64 super(owner); 65 } 66 67 /** 68 * Starts handshake 69 * 70 */ 71 @Override 72 public void start() { 73 if (session == null) { // initial handshake 74 session = findSessionToResume(); 75 } else { // start session renegotiation 76 if (clientHello != null && this.status != FINISHED) { 77 // current negotiation has not completed 78 return; // ignore 79 } 80 if (!session.isValid()) { 81 session = null; 82 } 83 } 84 if (session != null) { 85 isResuming = true; 86 } else if (parameters.getEnableSessionCreation()){ 87 isResuming = false; 88 session = new SSLSessionImpl(parameters.getSecureRandom()); 89 if (engineOwner != null) { 90 session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort()); 91 } else { 92 session.setPeer(socketOwner.getPeerHostName(), socketOwner.getPeerPort()); 93 } 94 session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols()); 95 recordProtocol.setVersion(session.protocol.version); 96 } else { 97 fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "SSL Session may not be created "); 98 } 99 startSession(); 100 } 101 102 /** 103 * Starts renegotiation on a new session 104 * 105 */ 106 private void renegotiateNewSession() { 107 if (parameters.getEnableSessionCreation()){ 108 isResuming = false; 109 session = new SSLSessionImpl(parameters.getSecureRandom()); 110 if (engineOwner != null) { 111 session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort()); 112 } else { 113 session.setPeer(socketOwner.getPeerHostName(), socketOwner.getPeerPort()); 114 } 115 session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols()); 116 recordProtocol.setVersion(session.protocol.version); 117 startSession(); 118 } else { 119 status = NOT_HANDSHAKING; 120 sendWarningAlert(AlertProtocol.NO_RENEGOTIATION); 121 } 122 } 123 124 /* 125 * Starts/resumes session 126 */ 127 private void startSession() { 128 CipherSuite[] cipher_suites; 129 if (isResuming) { 130 cipher_suites = new CipherSuite[] { session.cipherSuite }; 131 } else { 132 cipher_suites = parameters.getEnabledCipherSuitesMember(); 133 } 134 clientHello = new ClientHello(parameters.getSecureRandom(), 135 session.protocol.version, session.id, cipher_suites); 136 session.clientRandom = clientHello.random; 137 send(clientHello); 138 status = NEED_UNWRAP; 139 } 140 141 /** 142 * Processes inbound handshake messages 143 * @param bytes 144 */ 145 @Override 146 public void unwrap(byte[] bytes) { 147 if (this.delegatedTaskErr != null) { 148 Exception e = this.delegatedTaskErr; 149 this.delegatedTaskErr = null; 150 this.fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "Error in delegated task", e); 151 } 152 int handshakeType; 153 io_stream.append(bytes); 154 while (io_stream.available() > 0) { 155 io_stream.mark(); 156 int length; 157 try { 158 handshakeType = io_stream.read(); 159 length = io_stream.readUint24(); 160 if (io_stream.available() < length) { 161 io_stream.reset(); 162 return; 163 } 164 switch (handshakeType) { 165 case 0: // HELLO_REQUEST 166 // we don't need to take this message into account 167 // during FINISH message verification, so remove it 168 io_stream.removeFromMarkedPosition(); 169 if (clientHello != null 170 && (clientFinished == null || serverFinished == null)) { 171 //currently negotiating - ignore 172 break; 173 } 174 // renegotiate 175 if (session.isValid()) { 176 session = (SSLSessionImpl) session.clone(); 177 isResuming = true; 178 startSession(); 179 } else { 180 // if SSLSession is invalidated (e.g. timeout limit is 181 // exceeded) connection can't resume the session. 182 renegotiateNewSession(); 183 } 184 break; 185 case 2: // SERVER_HELLO 186 if (clientHello == null || serverHello != null) { 187 unexpectedMessage(); 188 return; 189 } 190 serverHello = new ServerHello(io_stream, length); 191 192 //check protocol version 193 ProtocolVersion servProt = ProtocolVersion.getByVersion(serverHello.server_version); 194 String[] enabled = parameters.getEnabledProtocols(); 195 find: { 196 for (int i = 0; i < enabled.length; i++) { 197 if (servProt.equals(ProtocolVersion.getByName(enabled[i]))) { 198 break find; 199 } 200 } 201 fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, 202 "Bad server hello protocol version"); 203 } 204 205 // check compression method 206 if (serverHello.compression_method != 0) { 207 fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, 208 "Bad server hello compression method"); 209 } 210 211 //check cipher_suite 212 CipherSuite[] enabledSuites = parameters.getEnabledCipherSuitesMember(); 213 find: { 214 for (int i = 0; i < enabledSuites.length; i++) { 215 if (serverHello.cipher_suite.equals(enabledSuites[i])) { 216 break find; 217 } 218 } 219 fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, 220 "Bad server hello cipher suite"); 221 } 222 223 if (isResuming) { 224 if (serverHello.session_id.length == 0) { 225 // server is not willing to establish the new connection 226 // using specified session 227 isResuming = false; 228 } else if (!Arrays.equals(serverHello.session_id, clientHello.session_id)) { 229 isResuming = false; 230 } else if (!session.protocol.equals(servProt)) { 231 fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, 232 "Bad server hello protocol version"); 233 } else if (!session.cipherSuite.equals(serverHello.cipher_suite)) { 234 fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, 235 "Bad server hello cipher suite"); 236 } 237 if (serverHello.server_version[1] == 1) { 238 computerReferenceVerifyDataTLS("server finished"); 239 } else { 240 computerReferenceVerifyDataSSLv3(SSLv3Constants.server); 241 } 242 } 243 session.protocol = servProt; 244 recordProtocol.setVersion(session.protocol.version); 245 session.cipherSuite = serverHello.cipher_suite; 246 session.id = serverHello.session_id.clone(); 247 session.serverRandom = serverHello.random; 248 break; 249 case 11: // CERTIFICATE 250 if (serverHello == null || serverKeyExchange != null 251 || serverCert != null || isResuming) { 252 unexpectedMessage(); 253 return; 254 } 255 serverCert = new CertificateMessage(io_stream, length); 256 break; 257 case 12: // SERVER_KEY_EXCHANGE 258 if (serverHello == null || serverKeyExchange != null 259 || isResuming) { 260 unexpectedMessage(); 261 return; 262 } 263 serverKeyExchange = new ServerKeyExchange(io_stream, 264 length, session.cipherSuite.keyExchange); 265 break; 266 case 13: // CERTIFICATE_REQUEST 267 if (serverCert == null || certificateRequest != null 268 || session.cipherSuite.isAnonymous() || isResuming) { 269 unexpectedMessage(); 270 return; 271 } 272 certificateRequest = new CertificateRequest(io_stream, length); 273 break; 274 case 14: // SERVER_HELLO_DONE 275 if (serverHello == null || serverHelloDone != null || isResuming) { 276 unexpectedMessage(); 277 return; 278 } 279 serverHelloDone = new ServerHelloDone(io_stream, length); 280 if (this.nonBlocking) { 281 delegatedTasks.add(new DelegatedTask(new Runnable() { 282 public void run() { 283 processServerHelloDone(); 284 } 285 }, this)); 286 return; 287 } 288 processServerHelloDone(); 289 break; 290 case 20: // FINISHED 291 if (!changeCipherSpecReceived) { 292 unexpectedMessage(); 293 return; 294 } 295 serverFinished = new Finished(io_stream, length); 296 verifyFinished(serverFinished.getData()); 297 session.lastAccessedTime = System.currentTimeMillis(); 298 session.context = parameters.getClientSessionContext(); 299 parameters.getClientSessionContext().putSession(session); 300 if (isResuming) { 301 sendChangeCipherSpec(); 302 } else { 303 session.lastAccessedTime = System.currentTimeMillis(); 304 status = FINISHED; 305 } 306 // XXX there is no cleanup work 307 break; 308 default: 309 unexpectedMessage(); 310 return; 311 } 312 } catch (IOException e) { 313 // io stream dosn't contain complete handshake message 314 io_stream.reset(); 315 return; 316 } 317 } 318 319 } 320 321 /** 322 * Processes SSLv2 Hello message. 323 * SSLv2 client hello message message is an unexpected message 324 * for client side of handshake protocol. 325 * See TLS 1.0 spec., E.1. Version 2 client hello 326 * @param bytes 327 */ 328 @Override 329 public void unwrapSSLv2(byte[] bytes) { 330 unexpectedMessage(); 331 } 332 333 /** 334 * Creates and sends Finished message 335 */ 336 @Override 337 protected void makeFinished() { 338 byte[] verify_data; 339 if (serverHello.server_version[1] == 1) { 340 verify_data = new byte[12]; 341 computerVerifyDataTLS("client finished", verify_data); 342 } else { 343 verify_data = new byte[36]; 344 computerVerifyDataSSLv3(SSLv3Constants.client, verify_data); 345 } 346 clientFinished = new Finished(verify_data); 347 send(clientFinished); 348 if (isResuming) { 349 session.lastAccessedTime = System.currentTimeMillis(); 350 status = FINISHED; 351 } else { 352 if (serverHello.server_version[1] == 1) { 353 computerReferenceVerifyDataTLS("server finished"); 354 } else { 355 computerReferenceVerifyDataSSLv3(SSLv3Constants.server); 356 } 357 status = NEED_UNWRAP; 358 } 359 } 360 361 /** 362 * Processes ServerHelloDone: makes verification of the server messages; sends 363 * client messages, computers masterSecret, sends ChangeCipherSpec 364 */ 365 void processServerHelloDone() { 366 PrivateKey clientKey = null; 367 368 if (serverCert != null) { 369 if (session.cipherSuite.isAnonymous()) { 370 unexpectedMessage(); 371 return; 372 } 373 verifyServerCert(); 374 } else { 375 if (!session.cipherSuite.isAnonymous()) { 376 unexpectedMessage(); 377 return; 378 } 379 } 380 381 // Client certificate 382 if (certificateRequest != null) { 383 X509Certificate[] certs = null; 384 // obtain certificates from key manager 385 String alias = null; 386 String[] certTypes = certificateRequest.getTypesAsString(); 387 X500Principal[] issuers = certificateRequest.certificate_authorities; 388 X509KeyManager km = parameters.getKeyManager(); 389 if (km instanceof X509ExtendedKeyManager) { 390 X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km; 391 if (this.socketOwner != null) { 392 alias = ekm.chooseClientAlias(certTypes, issuers, this.socketOwner); 393 } else { 394 alias = ekm.chooseEngineClientAlias(certTypes, issuers, this.engineOwner); 395 } 396 if (alias != null) { 397 certs = ekm.getCertificateChain(alias); 398 } 399 } else { 400 alias = km.chooseClientAlias(certTypes, issuers, this.socketOwner); 401 if (alias != null) { 402 certs = km.getCertificateChain(alias); 403 } 404 } 405 406 session.localCertificates = certs; 407 clientCert = new CertificateMessage(certs); 408 clientKey = km.getPrivateKey(alias); 409 send(clientCert); 410 } 411 // Client key exchange 412 if (session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA 413 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) { 414 // RSA encrypted premaster secret message 415 Cipher c; 416 try { 417 c = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 418 if (serverKeyExchange != null) { 419 if (!session.cipherSuite.isAnonymous()) { 420 DigitalSignature ds = new DigitalSignature(serverCert.getAuthType()); 421 ds.init(serverCert.certs[0]); 422 ds.update(clientHello.getRandom()); 423 ds.update(serverHello.getRandom()); 424 if (!serverKeyExchange.verifySignature(ds)) { 425 fatalAlert(AlertProtocol.DECRYPT_ERROR, "Cannot verify RSA params"); 426 return; 427 } 428 } 429 c.init(Cipher.WRAP_MODE, serverKeyExchange 430 .getRSAPublicKey()); 431 } else { 432 c.init(Cipher.WRAP_MODE, serverCert.certs[0]); 433 } 434 } catch (Exception e) { 435 fatalAlert(AlertProtocol.INTERNAL_ERROR, 436 "Unexpected exception", e); 437 return; 438 } 439 preMasterSecret = new byte[48]; 440 parameters.getSecureRandom().nextBytes(preMasterSecret); 441 System.arraycopy(clientHello.client_version, 0, preMasterSecret, 0, 2); 442 try { 443 clientKeyExchange = new ClientKeyExchange(c 444 .wrap(new SecretKeySpec(preMasterSecret, "preMasterSecret")), 445 serverHello.server_version[1] == 1); 446 } catch (Exception e) { 447 fatalAlert(AlertProtocol.INTERNAL_ERROR, 448 "Unexpected exception", e); 449 return; 450 } 451 } else if (session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS 452 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS_EXPORT 453 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA 454 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA_EXPORT 455 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DH_anon 456 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DH_anon_EXPORT) { 457 /* 458 * All other key exchanges should have had a DH key communicated via 459 * ServerKeyExchange beforehand. 460 */ 461 if (serverKeyExchange == null) { 462 fatalAlert(AlertProtocol.UNEXPECTED_MESSAGE, "Expected ServerKeyExchange"); 463 return; 464 } 465 if (session.cipherSuite.isAnonymous() != serverKeyExchange.isAnonymous()) { 466 fatalAlert(AlertProtocol.DECRYPT_ERROR, "Wrong type in ServerKeyExchange"); 467 return; 468 } 469 try { 470 if (!session.cipherSuite.isAnonymous()) { 471 DigitalSignature ds = new DigitalSignature(serverCert.getAuthType()); 472 ds.init(serverCert.certs[0]); 473 ds.update(clientHello.getRandom()); 474 ds.update(serverHello.getRandom()); 475 if (!serverKeyExchange.verifySignature(ds)) { 476 fatalAlert(AlertProtocol.DECRYPT_ERROR, "Cannot verify DH params"); 477 return; 478 } 479 } 480 KeyFactory kf = KeyFactory.getInstance("DH"); 481 KeyAgreement agreement = KeyAgreement.getInstance("DH"); 482 KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH"); 483 PublicKey serverDhPublic = kf.generatePublic(new DHPublicKeySpec( 484 serverKeyExchange.par3, serverKeyExchange.par1, 485 serverKeyExchange.par2)); 486 DHParameterSpec spec = new DHParameterSpec(serverKeyExchange.par1, 487 serverKeyExchange.par2); 488 kpg.initialize(spec); 489 KeyPair kp = kpg.generateKeyPair(); 490 DHPublicKey pubDhKey = (DHPublicKey) kp.getPublic(); 491 clientKeyExchange = new ClientKeyExchange(pubDhKey.getY()); 492 PrivateKey privDhKey = kp.getPrivate(); 493 agreement.init(privDhKey); 494 agreement.doPhase(serverDhPublic, true); 495 preMasterSecret = agreement.generateSecret(); 496 } catch (Exception e) { 497 fatalAlert(AlertProtocol.INTERNAL_ERROR, 498 "Unexpected exception", e); 499 return; 500 } 501 } else { 502 fatalAlert(AlertProtocol.DECRYPT_ERROR, "Unsupported handshake type"); 503 return; 504 } 505 506 if (clientKeyExchange != null) { 507 send(clientKeyExchange); 508 } 509 510 computerMasterSecret(); 511 512 // send certificate verify for all certificates except those containing 513 // fixed DH parameters 514 if (clientCert != null && clientCert.certs.length > 0 && !clientKeyExchange.isEmpty()) { 515 // Certificate verify 516 String authType = clientKey.getAlgorithm(); 517 DigitalSignature ds = new DigitalSignature(authType); 518 ds.init(clientKey); 519 520 if ("RSA".equals(authType)) { 521 ds.setMD5(io_stream.getDigestMD5()); 522 ds.setSHA(io_stream.getDigestSHA()); 523 } else if ("DSA".equals(authType)) { 524 ds.setSHA(io_stream.getDigestSHA()); 525 // The Signature should be empty in case of anonymous signature algorithm: 526 // } else if ("DH".equals(authType)) { 527 } 528 certificateVerify = new CertificateVerify(ds.sign()); 529 send(certificateVerify); 530 } 531 532 sendChangeCipherSpec(); 533 } 534 535 /* 536 * Verifies certificate path 537 */ 538 private void verifyServerCert() { 539 String authType = session.cipherSuite.getAuthType(serverKeyExchange != null); 540 if (authType == null) { 541 return; 542 } 543 String hostname = null; 544 if (engineOwner != null) { 545 hostname = engineOwner.getPeerHost(); 546 } else { 547 // we don't want to do an inet address lookup here in case we're talking to a proxy 548 hostname = socketOwner.getWrappedHostName(); 549 } 550 try { 551 X509TrustManager x509tm = parameters.getTrustManager(); 552 if (x509tm instanceof TrustManagerImpl) { 553 TrustManagerImpl tm = (TrustManagerImpl) x509tm; 554 tm.checkServerTrusted(serverCert.certs, authType, hostname); 555 } else { 556 x509tm.checkServerTrusted(serverCert.certs, authType); 557 } 558 } catch (CertificateException e) { 559 fatalAlert(AlertProtocol.BAD_CERTIFICATE, "Not trusted server certificate", e); 560 return; 561 } 562 session.peerCertificates = serverCert.certs; 563 } 564 565 /** 566 * Processes ChangeCipherSpec message 567 */ 568 @Override 569 public void receiveChangeCipherSpec() { 570 if (isResuming) { 571 if (serverHello == null) { 572 unexpectedMessage(); 573 } 574 } else if (clientFinished == null) { 575 unexpectedMessage(); 576 } 577 changeCipherSpecReceived = true; 578 } 579 580 // Find session to resume in client session context 581 private SSLSessionImpl findSessionToResume() { 582 String host = null; 583 int port = -1; 584 if (engineOwner != null) { 585 host = engineOwner.getPeerHost(); 586 port = engineOwner.getPeerPort(); 587 } else { 588 host = socketOwner.getPeerHostName(); 589 port = socketOwner.getPeerPort(); 590 } 591 if (host == null || port == -1) { 592 return null; // starts new session 593 } 594 595 ClientSessionContext context = parameters.getClientSessionContext(); 596 SSLSessionImpl session 597 = (SSLSessionImpl) context.getSession(host, port); 598 if (session != null) { 599 session = (SSLSessionImpl) session.clone(); 600 } 601 return session; 602 } 603 604 } 605