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