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.Key; 22import java.security.KeyFactory; 23import java.security.KeyPair; 24import java.security.KeyPairGenerator; 25import java.security.NoSuchAlgorithmException; 26import java.security.PrivateKey; 27import java.security.PublicKey; 28import java.security.cert.CertificateException; 29import java.security.cert.X509Certificate; 30import java.util.Arrays; 31import javax.crypto.Cipher; 32import javax.crypto.KeyAgreement; 33import javax.crypto.interfaces.DHKey; 34import javax.crypto.interfaces.DHPublicKey; 35import javax.crypto.spec.DHParameterSpec; 36import javax.crypto.spec.DHPublicKeySpec; 37import javax.crypto.spec.SecretKeySpec; 38import javax.net.ssl.X509ExtendedKeyManager; 39import javax.net.ssl.X509KeyManager; 40import javax.net.ssl.X509TrustManager; 41import 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 */ 56public 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 c.init(Cipher.WRAP_MODE, serverKeyExchange 420 .getRSAPublicKey()); 421 } else { 422 c.init(Cipher.WRAP_MODE, serverCert.certs[0]); 423 } 424 } catch (Exception e) { 425 fatalAlert(AlertProtocol.INTERNAL_ERROR, 426 "Unexpected exception", e); 427 return; 428 } 429 preMasterSecret = new byte[48]; 430 parameters.getSecureRandom().nextBytes(preMasterSecret); 431 System.arraycopy(clientHello.client_version, 0, preMasterSecret, 0, 2); 432 try { 433 clientKeyExchange = new ClientKeyExchange(c 434 .wrap(new SecretKeySpec(preMasterSecret, "preMasterSecret")), 435 serverHello.server_version[1] == 1); 436 } catch (Exception e) { 437 fatalAlert(AlertProtocol.INTERNAL_ERROR, 438 "Unexpected exception", e); 439 return; 440 } 441 } else { 442 try { 443 KeyFactory kf = KeyFactory.getInstance("DH"); 444 KeyAgreement agreement = KeyAgreement.getInstance("DH"); 445 KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH"); 446 PublicKey serverPublic; 447 DHParameterSpec spec; 448 if (serverKeyExchange != null) { 449 serverPublic = kf.generatePublic(new DHPublicKeySpec( 450 serverKeyExchange.par3, serverKeyExchange.par1, 451 serverKeyExchange.par2)); 452 spec = new DHParameterSpec(serverKeyExchange.par1, 453 serverKeyExchange.par2); 454 } else { 455 serverPublic = serverCert.certs[0].getPublicKey(); 456 spec = ((DHPublicKey) serverPublic).getParams(); 457 } 458 kpg.initialize(spec); 459 460 KeyPair kp = kpg.generateKeyPair(); 461 Key key = kp.getPublic(); 462 if (clientCert != null 463 && serverCert != null 464 && (session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA 465 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS)) { 466 PublicKey client_pk = clientCert.certs[0].getPublicKey(); 467 PublicKey server_pk = serverCert.certs[0].getPublicKey(); 468 if (client_pk instanceof DHKey 469 && server_pk instanceof DHKey) { 470 if (((DHKey) client_pk).getParams().getG().equals( 471 ((DHKey) server_pk).getParams().getG()) 472 && ((DHKey) client_pk).getParams().getP() 473 .equals(((DHKey) server_pk).getParams().getG())) { 474 // client cert message DH public key parameters 475 // matched those specified by the 476 // server in its certificate, 477 clientKeyExchange = new ClientKeyExchange(); // empty 478 } 479 } 480 } else { 481 clientKeyExchange = new ClientKeyExchange( 482 ((DHPublicKey) key).getY()); 483 } 484 key = kp.getPrivate(); 485 agreement.init(key); 486 agreement.doPhase(serverPublic, true); 487 preMasterSecret = agreement.generateSecret(); 488 } catch (Exception e) { 489 fatalAlert(AlertProtocol.INTERNAL_ERROR, 490 "Unexpected exception", e); 491 return; 492 } 493 } 494 if (clientKeyExchange != null) { 495 send(clientKeyExchange); 496 } 497 498 computerMasterSecret(); 499 500 // send certificate verify for all certificates except those containing 501 // fixed DH parameters 502 if (clientCert != null && clientCert.certs.length > 0 && !clientKeyExchange.isEmpty()) { 503 // Certificate verify 504 String authType = clientKey.getAlgorithm(); 505 DigitalSignature ds = new DigitalSignature(authType); 506 ds.init(clientKey); 507 508 if ("RSA".equals(authType)) { 509 ds.setMD5(io_stream.getDigestMD5()); 510 ds.setSHA(io_stream.getDigestSHA()); 511 } else if ("DSA".equals(authType)) { 512 ds.setSHA(io_stream.getDigestSHA()); 513 // The Signature should be empty in case of anonymous signature algorithm: 514 // } else if ("DH".equals(authType)) { 515 } 516 certificateVerify = new CertificateVerify(ds.sign()); 517 send(certificateVerify); 518 } 519 520 sendChangeCipherSpec(); 521 } 522 523 /* 524 * Verifies certificate path 525 */ 526 private void verifyServerCert() { 527 String authType = session.cipherSuite.getAuthType(serverKeyExchange != null); 528 if (authType == null) { 529 return; 530 } 531 String hostname = null; 532 if (engineOwner != null) { 533 hostname = engineOwner.getPeerHost(); 534 } else { 535 // we don't want to do an inet address lookup here in case we're talking to a proxy 536 hostname = socketOwner.getWrappedHostName(); 537 } 538 try { 539 X509TrustManager x509tm = parameters.getTrustManager(); 540 if (x509tm instanceof TrustManagerImpl) { 541 TrustManagerImpl tm = (TrustManagerImpl) x509tm; 542 tm.checkServerTrusted(serverCert.certs, authType, hostname); 543 } else { 544 x509tm.checkServerTrusted(serverCert.certs, authType); 545 } 546 } catch (CertificateException e) { 547 fatalAlert(AlertProtocol.BAD_CERTIFICATE, "Not trusted server certificate", e); 548 return; 549 } 550 session.peerCertificates = serverCert.certs; 551 } 552 553 /** 554 * Processes ChangeCipherSpec message 555 */ 556 @Override 557 public void receiveChangeCipherSpec() { 558 if (isResuming) { 559 if (serverHello == null) { 560 unexpectedMessage(); 561 } 562 } else if (clientFinished == null) { 563 unexpectedMessage(); 564 } 565 changeCipherSpecReceived = true; 566 } 567 568 // Find session to resume in client session context 569 private SSLSessionImpl findSessionToResume() { 570 String host = null; 571 int port = -1; 572 if (engineOwner != null) { 573 host = engineOwner.getPeerHost(); 574 port = engineOwner.getPeerPort(); 575 } else { 576 host = socketOwner.getPeerHostName(); 577 port = socketOwner.getPeerPort(); 578 } 579 if (host == null || port == -1) { 580 return null; // starts new session 581 } 582 583 ClientSessionContext context = parameters.getClientSessionContext(); 584 SSLSessionImpl session 585 = (SSLSessionImpl) context.getSession(host, port); 586 if (session != null) { 587 session = (SSLSessionImpl) session.clone(); 588 } 589 return session; 590 } 591 592} 593