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