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