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