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