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