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