OpenSSLSocketImpl.java revision e3a187163504f00c98bd75cbd8bcbdde123ae2cd
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package org.apache.harmony.xnet.provider.jsse; 18 19import java.io.IOException; 20import java.io.InputStream; 21import java.io.OutputStream; 22import java.net.InetAddress; 23import java.net.Socket; 24import java.net.SocketException; 25import java.security.PrivateKey; 26import java.security.SecureRandom; 27import java.security.cert.CertificateEncodingException; 28import java.security.cert.CertificateException; 29import java.security.cert.X509Certificate; 30import java.util.ArrayList; 31import java.util.concurrent.atomic.AtomicInteger; 32import java.util.logging.Logger; 33import javax.net.ssl.HandshakeCompletedEvent; 34import javax.net.ssl.HandshakeCompletedListener; 35import javax.net.ssl.SSLException; 36import javax.net.ssl.SSLPeerUnverifiedException; 37import javax.net.ssl.SSLSession; 38import javax.security.auth.x500.X500Principal; 39import org.apache.harmony.security.provider.cert.X509CertImpl; 40 41/** 42 * Implementation of the class OpenSSLSocketImpl 43 * based on OpenSSL. 44 * 45 * This class only supports SSLv3 and TLSv1. This should be documented elsewhere 46 * later, for example in the package.html or a separate reference document. 47 */ 48public class OpenSSLSocketImpl 49 extends javax.net.ssl.SSLSocket 50 implements NativeCrypto.SSLHandshakeCallbacks { 51 private int sslNativePointer; 52 private InputStream is; 53 private OutputStream os; 54 private final Object handshakeLock = new Object(); 55 private final Object readLock = new Object(); 56 private final Object writeLock = new Object(); 57 private SSLParameters sslParameters; 58 private String[] enabledProtocols; 59 private String[] enabledCipherSuites; 60 private OpenSSLSessionImpl sslSession; 61 private Socket socket; 62 private boolean autoClose; 63 private boolean handshakeStarted = false; 64 65 /** 66 * Not set to true until the update from native that tells us the 67 * full handshake is complete, since SSL_do_handshake can return 68 * before the handshake is completely done due to 69 * handshake_cutthrough support. 70 */ 71 private boolean handshakeCompleted = false; 72 73 private ArrayList<HandshakeCompletedListener> listeners; 74 private int timeout = 0; 75 // BEGIN android-added 76 private int handshakeTimeout = -1; // -1 = same as timeout; 0 = infinite 77 // END android-added 78 private String wrappedHost; 79 private int wrappedPort; 80 81 private static final AtomicInteger instanceCount = new AtomicInteger(0); 82 83 public static int getInstanceCount() { 84 return instanceCount.get(); 85 } 86 87 private static void updateInstanceCount(int amount) { 88 instanceCount.addAndGet(amount); 89 } 90 91 /** 92 * Class constructor with 1 parameter 93 * 94 * @param sslParameters Parameters for the SSL 95 * context 96 * @throws IOException if network fails 97 */ 98 protected OpenSSLSocketImpl(SSLParameters sslParameters) throws IOException { 99 super(); 100 init(sslParameters); 101 } 102 103 /** 104 * Create an OpenSSLSocketImpl from an OpenSSLServerSocketImpl 105 * 106 * @param sslParameters Parameters for the SSL 107 * context 108 * @throws IOException if network fails 109 */ 110 protected OpenSSLSocketImpl(SSLParameters sslParameters, 111 String[] enabledProtocols, 112 String[] enabledCipherSuites) throws IOException { 113 super(); 114 init(sslParameters, enabledProtocols, enabledCipherSuites); 115 } 116 117 /** 118 * Class constructor with 3 parameters 119 * 120 * @throws IOException if network fails 121 * @throws java.net.UnknownHostException host not defined 122 */ 123 protected OpenSSLSocketImpl(String host, int port, SSLParameters sslParameters) 124 throws IOException { 125 super(host, port); 126 init(sslParameters); 127 } 128 129 /** 130 * Class constructor with 3 parameters: 1st is InetAddress 131 * 132 * @throws IOException if network fails 133 * @throws java.net.UnknownHostException host not defined 134 */ 135 protected OpenSSLSocketImpl(InetAddress address, int port, SSLParameters sslParameters) 136 throws IOException { 137 super(address, port); 138 init(sslParameters); 139 } 140 141 142 /** 143 * Class constructor with 5 parameters: 1st is host 144 * 145 * @throws IOException if network fails 146 * @throws java.net.UnknownHostException host not defined 147 */ 148 protected OpenSSLSocketImpl(String host, int port, 149 InetAddress clientAddress, int clientPort, 150 SSLParameters sslParameters) 151 throws IOException { 152 super(host, port, clientAddress, clientPort); 153 init(sslParameters); 154 } 155 156 /** 157 * Class constructor with 5 parameters: 1st is InetAddress 158 * 159 * @throws IOException if network fails 160 * @throws java.net.UnknownHostException host not defined 161 */ 162 protected OpenSSLSocketImpl(InetAddress address, int port, 163 InetAddress clientAddress, int clientPort, 164 SSLParameters sslParameters) 165 throws IOException { 166 super(address, port, clientAddress, clientPort); 167 init(sslParameters); 168 } 169 170 /** 171 * Constructor with 5 parameters: 1st is socket. Enhances an existing socket 172 * with SSL functionality. Invoked via OpenSSLSocketImplWrapper constructor. 173 * 174 * @throws IOException if network fails 175 */ 176 protected OpenSSLSocketImpl(Socket socket, String host, int port, 177 boolean autoClose, SSLParameters sslParameters) throws IOException { 178 super(); 179 this.socket = socket; 180 this.timeout = socket.getSoTimeout(); 181 this.wrappedHost = host; 182 this.wrappedPort = port; 183 this.autoClose = autoClose; 184 init(sslParameters); 185 } 186 187 /** 188 * Initialize the SSL socket and set the certificates for the 189 * future handshaking. 190 */ 191 private void init(SSLParameters sslParameters) throws IOException { 192 init(sslParameters, 193 NativeCrypto.getSupportedProtocols(), 194 NativeCrypto.getDefaultCipherSuites()); 195 } 196 197 /** 198 * Initialize the SSL socket and set the certificates for the 199 * future handshaking. 200 */ 201 private void init(SSLParameters sslParameters, 202 String[] enabledProtocols, 203 String[] enabledCipherSuites) throws IOException { 204 this.sslParameters = sslParameters; 205 this.enabledProtocols = enabledProtocols; 206 this.enabledCipherSuites = enabledCipherSuites; 207 updateInstanceCount(1); 208 } 209 210 /** 211 * Gets the suitable session reference from the session cache container. 212 * 213 * @return OpenSSLSessionImpl 214 */ 215 private OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext) { 216 if (super.getInetAddress() == null || 217 super.getInetAddress().getHostAddress() == null || 218 super.getInetAddress().getHostName() == null) { 219 return null; 220 } 221 OpenSSLSessionImpl session = (OpenSSLSessionImpl) sessionContext.getSession( 222 super.getInetAddress().getHostName(), 223 super.getPort()); 224 if (session == null) { 225 return null; 226 } 227 228 String protocol = session.getProtocol(); 229 boolean protocolFound = false; 230 for (String enabledProtocol : enabledProtocols) { 231 if (protocol.equals(enabledProtocol)) { 232 protocolFound = true; 233 break; 234 } 235 } 236 if (!protocolFound) { 237 return null; 238 } 239 240 String cipherSuite = session.getCipherSuite(); 241 boolean cipherSuiteFound = false; 242 for (String enabledCipherSuite : enabledCipherSuites) { 243 if (cipherSuite.equals(enabledCipherSuite)) { 244 cipherSuiteFound = true; 245 break; 246 } 247 } 248 if (!cipherSuiteFound) { 249 return null; 250 } 251 252 return session; 253 } 254 255 /** 256 * Ensures that logger is lazily loaded. The outer class seems to load 257 * before logging is ready. 258 */ 259 static class LoggerHolder { 260 static final Logger logger = Logger.getLogger(OpenSSLSocketImpl.class.getName()); 261 } 262 263 /** 264 * Starts a TLS/SSL handshake on this connection using some native methods 265 * from the OpenSSL library. It can negotiate new encryption keys, change 266 * cipher suites, or initiate a new session. The certificate chain is 267 * verified if the correspondent property in java.Security is set. All 268 * listeners are notified at the end of the TLS/SSL handshake. 269 * 270 * @throws <code>IOException</code> if network fails 271 */ 272 public void startHandshake() throws IOException { 273 startHandshake(true); 274 } 275 276 /** 277 * Perform the handshake 278 * @param full If true, disable handshake cutthrough for a fully synchronous handshake 279 */ 280 public synchronized void startHandshake(boolean full) throws IOException { 281 synchronized (handshakeLock) { 282 if (!handshakeStarted) { 283 handshakeStarted = true; 284 } else { 285 return; 286 } 287 } 288 289 // note that this modifies the global seed, not something specific to the connection 290 final int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES; 291 final SecureRandom secureRandom = sslParameters.getSecureRandomMember(); 292 if (secureRandom == null) { 293 NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes); 294 } else { 295 NativeCrypto.RAND_seed(secureRandom.generateSeed(seedLengthInBytes)); 296 } 297 298 final boolean client = sslParameters.getUseClientMode(); 299 300 final int sslCtxNativePointer = (client) ? 301 sslParameters.getClientSessionContext().sslCtxNativePointer : 302 sslParameters.getServerSessionContext().sslCtxNativePointer; 303 304 this.sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer); 305 306 // setup server certificates and private keys. 307 // clients will receive a call back to request certificates. 308 if (!client) { 309 for (String keyType : NativeCrypto.KEY_TYPES) { 310 setCertificate(sslParameters.getKeyManager().chooseServerAlias(keyType, 311 null, 312 this)); 313 } 314 } 315 316 NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols); 317 NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites); 318 319 boolean enableSessionCreation = sslParameters.getEnableSessionCreation(); 320 if (!enableSessionCreation) { 321 NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer, 322 enableSessionCreation); 323 } 324 325 AbstractSessionContext sessionContext; 326 OpenSSLSessionImpl session; 327 if (client) { 328 // look for client session to reuse 329 ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext(); 330 sessionContext = clientSessionContext; 331 session = getCachedClientSession(clientSessionContext); 332 if (session != null) { 333 NativeCrypto.SSL_set_session(sslNativePointer, session.sslSessionNativePointer); 334 } 335 } else { 336 sessionContext = sslParameters.getServerSessionContext(); 337 session = null; 338 } 339 340 // setup peer certificate verification 341 if (client) { 342 // TODO support for anonymous cipher would require us to 343 // conditionally use SSL_VERIFY_NONE 344 } else { 345 // needing client auth takes priority... 346 if (sslParameters.getNeedClientAuth()) { 347 NativeCrypto.SSL_set_verify(sslNativePointer, 348 NativeCrypto.SSL_VERIFY_PEER| 349 NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT); 350 // ... over just wanting it... 351 } else if (sslParameters.getWantClientAuth()) { 352 NativeCrypto.SSL_set_verify(sslNativePointer, 353 NativeCrypto.SSL_VERIFY_PEER); 354 } 355 // ... and it defaults properly so we don't need call SSL_set_verify in the common case. 356 357 // TODO Need to call SSL_CTX_set_client_CA_list to notify trusted issuers to client 358 // 359 // From SSL_CTX_load_verify_locations(3SSL) 360 // In server mode, when requesting a client 361 // certificate, the server must send the list of CAs 362 // of which it will accept client certificates. This 363 // list is not influenced by the contents of CAfile or 364 // CApath and must explicitly be set using the 365 // SSL_CTX_set_client_CA_list(3) family of functions. 366 // 367 // We can get the list from sslParameters.getTrustManager().getAcceptedIssuers() 368 } 369 370 if (client && full) { 371 // we want to do a full synchronous handshake, so turn off cutthrough 372 NativeCrypto.SSL_clear_mode(sslNativePointer, 373 NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH); 374 } 375 376 // BEGIN android-added 377 // Temporarily use a different timeout for the handshake process 378 int savedTimeout = timeout; 379 if (handshakeTimeout >= 0) { 380 setSoTimeout(handshakeTimeout); 381 } 382 // END android-added 383 384 385 Socket socket = this.socket != null ? this.socket : this; 386 int sslSessionNativePointer; 387 try { 388 sslSessionNativePointer 389 = NativeCrypto.SSL_do_handshake(sslNativePointer, socket, this, timeout, client); 390 } catch (CertificateException e) { 391 throw new SSLPeerUnverifiedException(e.getMessage()); 392 } 393 byte[] sessionId = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer); 394 sslSession = (OpenSSLSessionImpl) sessionContext.getSession(sessionId); 395 if (sslSession != null) { 396 sslSession.lastAccessedTime = System.currentTimeMillis(); 397 LoggerHolder.logger.fine("Reused cached session for " 398 + getInetAddress() + "."); 399 NativeCrypto.SSL_SESSION_free(sslSessionNativePointer); 400 } else { 401 if (!enableSessionCreation) { 402 // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled 403 throw new IllegalStateException("SSL Session may not be created"); 404 } 405 byte[][] localCertificatesBytes = NativeCrypto.SSL_get_certificate(sslNativePointer); 406 X509Certificate[] localCertificates; 407 if (localCertificatesBytes == null) { 408 localCertificates = null; 409 } else { 410 localCertificates = new X509Certificate[localCertificatesBytes.length]; 411 for (int i = 0; i < localCertificatesBytes.length; i++) { 412 localCertificates[i] = new X509CertImpl(localCertificatesBytes[i]); 413 } 414 } 415 416 if (wrappedHost == null) { 417 sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates, 418 super.getInetAddress().getHostName(), 419 super.getPort(), sessionContext); 420 } else { 421 sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates, 422 wrappedHost, wrappedPort, 423 sessionContext); 424 } 425 // if not, putSession later in handshakeCompleted() callback 426 if (handshakeCompleted) { 427 sessionContext.putSession(sslSession); 428 } 429 LoggerHolder.logger.fine("Created new session for " 430 + getInetAddress().getHostName() + "."); 431 } 432 433 // BEGIN android-added 434 // Restore the original timeout now that the handshake is complete 435 if (handshakeTimeout >= 0) { 436 setSoTimeout(savedTimeout); 437 } 438 // END android-added 439 440 // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback 441 if (handshakeCompleted) { 442 notifyHandshakeCompletedListeners(); 443 } 444 445 } 446 447 private void setCertificate(String alias) throws IOException { 448 if (alias == null) { 449 return; 450 } 451 452 PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias); 453 byte[] privateKeyBytes = privateKey.getEncoded(); 454 NativeCrypto.SSL_use_PrivateKey(sslNativePointer, privateKeyBytes); 455 456 X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias); 457 byte[][] certificateBytes = new byte[certificates.length][]; 458 for (int i = 0; i < certificates.length; i++) { 459 try { 460 certificateBytes[i] = certificates[i].getEncoded(); 461 } catch (CertificateEncodingException e) { 462 throw new IOException("Problem encoding certificate " + certificates[i], e); 463 } 464 } 465 NativeCrypto.SSL_use_certificate(sslNativePointer, certificateBytes); 466 467 // checks the last installed private key and certificate, 468 // so need to do this once per loop iteration 469 NativeCrypto.SSL_check_private_key(sslNativePointer); 470 } 471 472 /** 473 * Implementation of NativeCrypto.SSLHandshakeCallbacks 474 * invoked via JNI from client_cert_cb 475 */ 476 public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) 477 throws IOException { 478 479 String[] keyTypes = new String[keyTypeBytes.length]; 480 for (int i = 0; i < keyTypeBytes.length; i++) { 481 keyTypes[i] = NativeCrypto.keyType(keyTypeBytes[i]); 482 } 483 484 X500Principal[] issuers; 485 if (asn1DerEncodedPrincipals == null) { 486 issuers = null; 487 } else { 488 issuers = new X500Principal[asn1DerEncodedPrincipals.length]; 489 for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) { 490 issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]); 491 } 492 } 493 494 setCertificate(sslParameters.getKeyManager().chooseClientAlias(keyTypes, issuers, this)); 495 } 496 497 /** 498 * Implementation of NativeCrypto.SSLHandshakeCallbacks 499 * invoked via JNI from info_callback 500 */ 501 public void handshakeCompleted() { 502 handshakeCompleted = true; 503 504 // If sslSession is null, the handshake was completed during 505 // the call to NativeCrypto.SSL_do_handshake and not during a 506 // later read operation. That means we do not need to fixup 507 // the SSLSession and session cache or notify 508 // HandshakeCompletedListeners, it will be done in 509 // startHandshake. 510 if (sslSession == null) { 511 return; 512 } 513 514 // reset session id from the native pointer and update the 515 // appropriate cache. 516 sslSession.resetId(); 517 AbstractSessionContext sessionContext = 518 (sslParameters.getUseClientMode()) 519 ? sslParameters.getClientSessionContext() 520 : sslParameters.getServerSessionContext(); 521 sessionContext.putSession(sslSession); 522 523 // let listeners know we are finally done 524 notifyHandshakeCompletedListeners(); 525 } 526 527 private void notifyHandshakeCompletedListeners() { 528 if (listeners != null && !listeners.isEmpty()) { 529 // notify the listeners 530 HandshakeCompletedEvent event = 531 new HandshakeCompletedEvent(this, sslSession); 532 for (HandshakeCompletedListener listener : listeners) { 533 try { 534 listener.handshakeCompleted(event); 535 } catch (RuntimeException e) { 536 // The RI runs the handlers in a separate thread, 537 // which we do not. But we try to preserve their 538 // behavior of logging a problem and not killing 539 // the handshaking thread just because a listener 540 // has a problem. 541 Thread thread = Thread.currentThread(); 542 thread.getUncaughtExceptionHandler().uncaughtException(thread, e); 543 } 544 } 545 } 546 } 547 548 /** 549 * Implementation of NativeCrypto.SSLHandshakeCallbacks 550 * 551 * @param bytes An array of ASN.1 DER encoded certficates 552 * @param authMethod auth algorithm name 553 * 554 * @throws CertificateException if the certificate is untrusted 555 */ 556 @SuppressWarnings("unused") 557 public void verifyCertificateChain(byte[][] bytes, String authMethod) 558 throws CertificateException { 559 try { 560 if (bytes == null || bytes.length == 0) { 561 throw new SSLException("Peer sent no certificate"); 562 } 563 X509Certificate[] peerCertificateChain = new X509Certificate[bytes.length]; 564 for (int i = 0; i < bytes.length; i++) { 565 peerCertificateChain[i] = 566 new X509CertImpl( 567 javax.security.cert.X509Certificate.getInstance(bytes[i]).getEncoded()); 568 } 569 boolean client = sslParameters.getUseClientMode(); 570 if (client) { 571 sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain, 572 authMethod); 573 } else { 574 sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain, 575 authMethod); 576 } 577 578 } catch (CertificateException e) { 579 throw e; 580 } catch (Exception e) { 581 throw new RuntimeException(e); 582 } 583 } 584 585 /** 586 * Returns an input stream for this SSL socket using native calls to the 587 * OpenSSL library. 588 * 589 * @return: an input stream for reading bytes from this socket. 590 * @throws: <code>IOException</code> if an I/O error occurs when creating 591 * the input stream, the socket is closed, the socket is not 592 * connected, or the socket input has been shutdown. 593 */ 594 public InputStream getInputStream() throws IOException { 595 synchronized(this) { 596 if (is == null) { 597 is = new SSLInputStream(); 598 } 599 600 return is; 601 } 602 } 603 604 /** 605 * Returns an output stream for this SSL socket using native calls to the 606 * OpenSSL library. 607 * 608 * @return an output stream for writing bytes to this socket. 609 * @throws <code>IOException</code> if an I/O error occurs when creating 610 * the output stream, or no connection to the socket exists. 611 */ 612 public OutputStream getOutputStream() throws IOException { 613 synchronized(this) { 614 if (os == null) { 615 os = new SSLOutputStream(); 616 } 617 618 return os; 619 } 620 } 621 622 /** 623 * This method is not supported for this SSLSocket implementation 624 * because reading from an SSLSocket may involve writing to the 625 * network. 626 */ 627 public void shutdownInput() throws IOException { 628 throw new UnsupportedOperationException(); 629 } 630 631 /** 632 * This method is not supported for this SSLSocket implementation 633 * because writing to an SSLSocket may involve reading from the 634 * network. 635 */ 636 public void shutdownOutput() throws IOException { 637 throw new UnsupportedOperationException(); 638 } 639 640 /** 641 * This inner class provides input data stream functionality 642 * for the OpenSSL native implementation. It is used to 643 * read data received via SSL protocol. 644 */ 645 private class SSLInputStream extends InputStream { 646 SSLInputStream() throws IOException { 647 /** 648 /* Note: When startHandshake() throws an exception, no 649 * SSLInputStream object will be created. 650 */ 651 OpenSSLSocketImpl.this.startHandshake(false); 652 } 653 654 /** 655 * Reads one byte. If there is no data in the underlying buffer, 656 * this operation can block until the data will be 657 * available. 658 * @return read value. 659 * @throws <code>IOException</code> 660 */ 661 public int read() throws IOException { 662 synchronized(readLock) { 663 return NativeCrypto.SSL_read_byte(sslNativePointer, timeout); 664 } 665 } 666 667 /** 668 * Method acts as described in spec for superclass. 669 * @see java.io.InputStream#read(byte[],int,int) 670 */ 671 public int read(byte[] b, int off, int len) throws IOException { 672 synchronized(readLock) { 673 return NativeCrypto.SSL_read(sslNativePointer, b, off, len, timeout); 674 } 675 } 676 } 677 678 /** 679 * This inner class provides output data stream functionality 680 * for the OpenSSL native implementation. It is used to 681 * write data according to the encryption parameters given in SSL context. 682 */ 683 private class SSLOutputStream extends OutputStream { 684 SSLOutputStream() throws IOException { 685 /** 686 /* Note: When startHandshake() throws an exception, no 687 * SSLOutputStream object will be created. 688 */ 689 OpenSSLSocketImpl.this.startHandshake(false); 690 } 691 692 /** 693 * Method acts as described in spec for superclass. 694 * @see java.io.OutputStream#write(int) 695 */ 696 public void write(int b) throws IOException { 697 synchronized(writeLock) { 698 NativeCrypto.SSL_write_byte(sslNativePointer, b); 699 } 700 } 701 702 /** 703 * Method acts as described in spec for superclass. 704 * @see java.io.OutputStream#write(byte[],int,int) 705 */ 706 public void write(byte[] b, int start, int len) throws IOException { 707 synchronized(writeLock) { 708 NativeCrypto.SSL_write(sslNativePointer, b, start, len); 709 } 710 } 711 } 712 713 714 /** 715 * The SSL session used by this connection is returned. The SSL session 716 * determines which cipher suite should be used by all connections within 717 * that session and which identities have the session's client and server. 718 * This method starts the SSL handshake. 719 * @return the SSLSession. 720 * @throws <code>IOException</code> if the handshake fails 721 */ 722 public SSLSession getSession() { 723 try { 724 startHandshake(true); 725 } catch (IOException e) { 726 // return an invalid session with 727 // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL" 728 return SSLSessionImpl.NULL_SESSION; 729 } 730 return sslSession; 731 } 732 733 /** 734 * Registers a listener to be notified that a SSL handshake 735 * was successfully completed on this connection. 736 * @throws <code>IllegalArgumentException</code> if listener is null. 737 */ 738 public void addHandshakeCompletedListener( 739 HandshakeCompletedListener listener) { 740 if (listener == null) { 741 throw new IllegalArgumentException("Provided listener is null"); 742 } 743 if (listeners == null) { 744 listeners = new ArrayList(); 745 } 746 listeners.add(listener); 747 } 748 749 /** 750 * The method removes a registered listener. 751 * @throws IllegalArgumentException if listener is null or not registered 752 */ 753 public void removeHandshakeCompletedListener( 754 HandshakeCompletedListener listener) { 755 if (listener == null) { 756 throw new IllegalArgumentException("Provided listener is null"); 757 } 758 if (listeners == null) { 759 throw new IllegalArgumentException( 760 "Provided listener is not registered"); 761 } 762 if (!listeners.remove(listener)) { 763 throw new IllegalArgumentException( 764 "Provided listener is not registered"); 765 } 766 } 767 768 /** 769 * Returns true if new SSL sessions may be established by this socket. 770 * 771 * @return true if the session may be created; false if a session already 772 * exists and must be resumed. 773 */ 774 public boolean getEnableSessionCreation() { 775 return sslParameters.getEnableSessionCreation(); 776 } 777 778 /** 779 * Set a flag for the socket to inhibit or to allow the creation of a new 780 * SSL sessions. If the flag is set to false, and there are no actual 781 * sessions to resume, then there will be no successful handshaking. 782 * 783 * @param flag true if session may be created; false 784 * if a session already exists and must be resumed. 785 */ 786 public void setEnableSessionCreation(boolean flag) { 787 sslParameters.setEnableSessionCreation(flag); 788 } 789 790 /** 791 * The names of the cipher suites which could be used by the SSL connection 792 * are returned. 793 * @return an array of cipher suite names 794 */ 795 public String[] getSupportedCipherSuites() { 796 return NativeCrypto.getSupportedCipherSuites(); 797 } 798 799 /** 800 * The names of the cipher suites that are in use in the actual the SSL 801 * connection are returned. 802 * 803 * @return an array of cipher suite names 804 */ 805 public String[] getEnabledCipherSuites() { 806 return enabledCipherSuites.clone(); 807 } 808 809 /** 810 * This method enables the cipher suites listed by 811 * getSupportedCipherSuites(). 812 * 813 * @param suites names of all the cipher suites to 814 * put on use 815 * @throws IllegalArgumentException when one or more of the 816 * ciphers in array suites are not supported, or when the array 817 * is null. 818 */ 819 public void setEnabledCipherSuites(String[] suites) { 820 enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites); 821 } 822 823 /** 824 * The names of the protocols' versions that may be used on this SSL 825 * connection. 826 * @return an array of protocols names 827 */ 828 public String[] getSupportedProtocols() { 829 return NativeCrypto.getSupportedProtocols(); 830 } 831 832 /** 833 * The names of the protocols' versions that are in use on this SSL 834 * connection. 835 * 836 * @return an array of protocols names 837 */ 838 @Override 839 public String[] getEnabledProtocols() { 840 return enabledProtocols.clone(); 841 } 842 843 /** 844 * This method enables the protocols' versions listed by 845 * getSupportedProtocols(). 846 * 847 * @param protocols The names of all the protocols to put on use 848 * 849 * @throws IllegalArgumentException when one or more of the names in the 850 * array are not supported, or when the array is null. 851 */ 852 @Override 853 public synchronized void setEnabledProtocols(String[] protocols) { 854 enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols); 855 } 856 857 /** 858 * This method gives true back if the SSL socket is set to client mode. 859 * 860 * @return true if the socket should do the handshaking as client. 861 */ 862 public boolean getUseClientMode() { 863 return sslParameters.getUseClientMode(); 864 } 865 866 /** 867 * This method set the actual SSL socket to client mode. 868 * 869 * @param mode true if the socket starts in client 870 * mode 871 * @throws IllegalArgumentException if mode changes during 872 * handshake. 873 */ 874 public synchronized void setUseClientMode(boolean mode) { 875 if (handshakeStarted) { 876 throw new IllegalArgumentException( 877 "Could not change the mode after the initial handshake has begun."); 878 } 879 sslParameters.setUseClientMode(mode); 880 } 881 882 /** 883 * Returns true if the SSL socket requests client's authentication. Relevant 884 * only for server sockets! 885 * 886 * @return true if client authentication is desired, false if not. 887 */ 888 public boolean getWantClientAuth() { 889 return sslParameters.getWantClientAuth(); 890 } 891 892 /** 893 * Returns true if the SSL socket needs client's authentication. Relevant 894 * only for server sockets! 895 * 896 * @return true if client authentication is desired, false if not. 897 */ 898 public boolean getNeedClientAuth() { 899 return sslParameters.getNeedClientAuth(); 900 } 901 902 /** 903 * Sets the SSL socket to use client's authentication. Relevant only for 904 * server sockets! 905 * 906 * @param need true if client authentication is 907 * desired, false if not. 908 */ 909 public void setNeedClientAuth(boolean need) { 910 sslParameters.setNeedClientAuth(need); 911 } 912 913 /** 914 * Sets the SSL socket to use client's authentication. Relevant only for 915 * server sockets! Notice that in contrast to setNeedClientAuth(..) this 916 * method will continue the negotiation if the client decide not to send 917 * authentication credentials. 918 * 919 * @param want true if client authentication is 920 * desired, false if not. 921 */ 922 public void setWantClientAuth(boolean want) { 923 sslParameters.setWantClientAuth(want); 924 } 925 926 /** 927 * This method is not supported for SSLSocket implementation. 928 */ 929 public void sendUrgentData(int data) throws IOException { 930 throw new SocketException( 931 "Method sendUrgentData() is not supported."); 932 } 933 934 /** 935 * This method is not supported for SSLSocket implementation. 936 */ 937 public void setOOBInline(boolean on) throws SocketException { 938 throw new SocketException( 939 "Methods sendUrgentData, setOOBInline are not supported."); 940 } 941 942 /** 943 * Set the read timeout on this socket. The SO_TIMEOUT option, is specified 944 * in milliseconds. The read operation will block indefinitely for a zero 945 * value. 946 * 947 * @param timeout the read timeout value 948 * @throws SocketException if an error occurs setting the option 949 */ 950 public synchronized void setSoTimeout(int timeout) throws SocketException { 951 super.setSoTimeout(timeout); 952 this.timeout = timeout; 953 } 954 955 // BEGIN android-added 956 /** 957 * Set the handshake timeout on this socket. This timeout is specified in 958 * milliseconds and will be used only during the handshake process. 959 * 960 * @param timeout the handshake timeout value 961 */ 962 public synchronized void setHandshakeTimeout(int timeout) throws SocketException { 963 this.handshakeTimeout = timeout; 964 } 965 // END android-added 966 967 /** 968 * Closes the SSL socket. Once closed, a socket is not available for further 969 * use anymore under any circumstance. A new socket must be created. 970 * 971 * @throws <code>IOException</code> if an I/O error happens during the 972 * socket's closure. 973 */ 974 public void close() throws IOException { 975 // TODO: Close SSL sockets using a background thread so they close 976 // gracefully. 977 978 synchronized (handshakeLock) { 979 if (!handshakeStarted) { 980 // prevent further attemps to start handshake 981 handshakeStarted = true; 982 983 synchronized (this) { 984 free(); 985 986 if (socket != null) { 987 if (autoClose && !socket.isClosed()) socket.close(); 988 } else { 989 if (!super.isClosed()) super.close(); 990 } 991 } 992 993 return; 994 } 995 } 996 997 NativeCrypto.SSL_interrupt(sslNativePointer); 998 999 synchronized (this) { 1000 synchronized (writeLock) { 1001 synchronized (readLock) { 1002 1003 IOException pendingException = null; 1004 1005 // Shut down the SSL connection, per se. 1006 try { 1007 if (handshakeStarted) { 1008 NativeCrypto.SSL_shutdown(sslNativePointer); 1009 } 1010 } catch (IOException ex) { 1011 /* 1012 * Note the exception at this point, but try to continue 1013 * to clean the rest of this all up before rethrowing. 1014 */ 1015 pendingException = ex; 1016 } 1017 1018 /* 1019 * Even if the above call failed, it is still safe to free 1020 * the native structs, and we need to do so lest we leak 1021 * memory. 1022 */ 1023 free(); 1024 1025 if (socket != null) { 1026 if (autoClose && !socket.isClosed()) 1027 socket.close(); 1028 } else { 1029 if (!super.isClosed()) 1030 super.close(); 1031 } 1032 1033 if (pendingException != null) { 1034 throw pendingException; 1035 } 1036 } 1037 } 1038 } 1039 } 1040 1041 private void free() { 1042 if (sslNativePointer == 0) { 1043 return; 1044 } 1045 NativeCrypto.SSL_free(sslNativePointer); 1046 sslNativePointer = 0; 1047 } 1048 1049 protected void finalize() throws IOException { 1050 /* 1051 * Just worry about our own state. Notably we do not try and 1052 * close anything. The SocketImpl, either our own 1053 * PlainSocketImpl, or the Socket we are wrapping, will do 1054 * that. This might mean we do not properly SSL_shutdown, but 1055 * if you want to do that, properly close the socket yourself. 1056 * 1057 * The reason why we don't try to SSL_shutdown, is that there 1058 * can be a race between finalizers where the PlainSocketImpl 1059 * finalizer runs first and closes the socket. However, in the 1060 * meanwhile, the underlying file descriptor could be reused 1061 * for another purpose. If we call SSL_shutdown, the 1062 * underlying socket BIOs still have the old file descriptor 1063 * and will write the close notify to some unsuspecting 1064 * reader. 1065 */ 1066 updateInstanceCount(-1); 1067 free(); 1068 } 1069} 1070