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