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