1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.apache.harmony.xnet.provider.jsse; 18 19 import dalvik.system.BlockGuard; 20 import dalvik.system.CloseGuard; 21 import java.io.FileDescriptor; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.io.OutputStream; 25 import java.net.InetAddress; 26 import java.net.Socket; 27 import java.net.SocketException; 28 import java.security.PrivateKey; 29 import java.security.SecureRandom; 30 import java.security.cert.CertificateEncodingException; 31 import java.security.cert.CertificateException; 32 import java.security.cert.X509Certificate; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.HashSet; 36 import java.util.Set; 37 import javax.net.ssl.HandshakeCompletedEvent; 38 import javax.net.ssl.HandshakeCompletedListener; 39 import javax.net.ssl.SSLException; 40 import javax.net.ssl.SSLHandshakeException; 41 import javax.net.ssl.SSLProtocolException; 42 import javax.net.ssl.SSLSession; 43 import javax.net.ssl.X509TrustManager; 44 import javax.security.auth.x500.X500Principal; 45 import libcore.io.Streams; 46 import 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 */ 59 public 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 private void checkOpen() throws SocketException { 246 if (isClosed()) { 247 throw new SocketException("Socket is closed"); 248 } 249 } 250 251 /** 252 * Starts a TLS/SSL handshake on this connection using some native methods 253 * from the OpenSSL library. It can negotiate new encryption keys, change 254 * cipher suites, or initiate a new session. The certificate chain is 255 * verified if the correspondent property in java.Security is set. All 256 * listeners are notified at the end of the TLS/SSL handshake. 257 */ 258 @Override public synchronized void startHandshake() throws IOException { 259 synchronized (handshakeLock) { 260 checkOpen(); 261 if (!handshakeStarted) { 262 handshakeStarted = true; 263 } else { 264 return; 265 } 266 } 267 268 // note that this modifies the global seed, not something specific to the connection 269 final int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES; 270 final SecureRandom secureRandom = sslParameters.getSecureRandomMember(); 271 if (secureRandom == null) { 272 NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes); 273 } else { 274 NativeCrypto.RAND_seed(secureRandom.generateSeed(seedLengthInBytes)); 275 } 276 277 final boolean client = sslParameters.getUseClientMode(); 278 279 final int sslCtxNativePointer = (client) ? 280 sslParameters.getClientSessionContext().sslCtxNativePointer : 281 sslParameters.getServerSessionContext().sslCtxNativePointer; 282 283 this.sslNativePointer = 0; 284 boolean exception = true; 285 try { 286 sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer); 287 guard.open("close"); 288 289 if (npnProtocols != null) { 290 NativeCrypto.SSL_CTX_enable_npn(sslCtxNativePointer); 291 } 292 293 // setup server certificates and private keys. 294 // clients will receive a call back to request certificates. 295 if (!client) { 296 Set<String> keyTypes = new HashSet<String>(); 297 for (String enabledCipherSuite : enabledCipherSuites) { 298 if (enabledCipherSuite.equals(NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { 299 continue; 300 } 301 String keyType = CipherSuite.getByName(enabledCipherSuite).getServerKeyType(); 302 if (keyType != null) { 303 keyTypes.add(keyType); 304 } 305 } 306 for (String keyType : keyTypes) { 307 try { 308 setCertificate(sslParameters.getKeyManager().chooseServerAlias(keyType, 309 null, 310 this)); 311 } catch (CertificateEncodingException e) { 312 throw new IOException(e); 313 } 314 } 315 } 316 317 NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols); 318 NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites); 319 if (enabledCompressionMethods.length != 0) { 320 NativeCrypto.setEnabledCompressionMethods(sslNativePointer, 321 enabledCompressionMethods); 322 } 323 if (useSessionTickets) { 324 NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET); 325 } 326 if (hostname != null) { 327 NativeCrypto.SSL_set_tlsext_host_name(sslNativePointer, hostname); 328 } 329 330 boolean enableSessionCreation = sslParameters.getEnableSessionCreation(); 331 if (!enableSessionCreation) { 332 NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer, 333 enableSessionCreation); 334 } 335 336 AbstractSessionContext sessionContext; 337 if (client) { 338 // look for client session to reuse 339 ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext(); 340 sessionContext = clientSessionContext; 341 OpenSSLSessionImpl session = getCachedClientSession(clientSessionContext); 342 if (session != null) { 343 NativeCrypto.SSL_set_session(sslNativePointer, 344 session.sslSessionNativePointer); 345 } 346 } else { 347 sessionContext = sslParameters.getServerSessionContext(); 348 } 349 350 // setup peer certificate verification 351 if (client) { 352 // TODO support for anonymous cipher would require us to 353 // conditionally use SSL_VERIFY_NONE 354 } else { 355 // needing client auth takes priority... 356 boolean certRequested; 357 if (sslParameters.getNeedClientAuth()) { 358 NativeCrypto.SSL_set_verify(sslNativePointer, 359 NativeCrypto.SSL_VERIFY_PEER 360 | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT); 361 certRequested = true; 362 // ... over just wanting it... 363 } else if (sslParameters.getWantClientAuth()) { 364 NativeCrypto.SSL_set_verify(sslNativePointer, 365 NativeCrypto.SSL_VERIFY_PEER); 366 certRequested = true; 367 // ... and it defaults properly so don't call SSL_set_verify in the common case. 368 } else { 369 certRequested = false; 370 } 371 372 if (certRequested) { 373 X509TrustManager trustManager = sslParameters.getTrustManager(); 374 X509Certificate[] issuers = trustManager.getAcceptedIssuers(); 375 if (issuers != null && issuers.length != 0) { 376 byte[][] issuersBytes; 377 try { 378 issuersBytes = NativeCrypto.encodeIssuerX509Principals(issuers); 379 } catch (CertificateEncodingException e) { 380 throw new IOException("Problem encoding principals", e); 381 } 382 NativeCrypto.SSL_set_client_CA_list(sslNativePointer, issuersBytes); 383 } 384 } 385 } 386 387 // Temporarily use a different timeout for the handshake process 388 int savedTimeoutMilliseconds = getSoTimeout(); 389 if (handshakeTimeoutMilliseconds >= 0) { 390 setSoTimeout(handshakeTimeoutMilliseconds); 391 } 392 393 int sslSessionNativePointer; 394 try { 395 sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer, 396 socket.getFileDescriptor$(), this, getSoTimeout(), client, npnProtocols); 397 } catch (CertificateException e) { 398 SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage()); 399 wrapper.initCause(e); 400 throw wrapper; 401 } 402 byte[] sessionId = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer); 403 sslSession = (OpenSSLSessionImpl) sessionContext.getSession(sessionId); 404 if (sslSession != null) { 405 sslSession.lastAccessedTime = System.currentTimeMillis(); 406 NativeCrypto.SSL_SESSION_free(sslSessionNativePointer); 407 } else { 408 if (!enableSessionCreation) { 409 // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled 410 throw new IllegalStateException("SSL Session may not be created"); 411 } 412 X509Certificate[] localCertificates 413 = createCertChain(NativeCrypto.SSL_get_certificate(sslNativePointer)); 414 X509Certificate[] peerCertificates 415 = createCertChain(NativeCrypto.SSL_get_peer_cert_chain(sslNativePointer)); 416 sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates, 417 peerCertificates, getPeerHostName(), getPeerPort(), sessionContext); 418 // if not, putSession later in handshakeCompleted() callback 419 if (handshakeCompleted) { 420 sessionContext.putSession(sslSession); 421 } 422 } 423 424 // Restore the original timeout now that the handshake is complete 425 if (handshakeTimeoutMilliseconds >= 0) { 426 setSoTimeout(savedTimeoutMilliseconds); 427 } 428 429 // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback 430 if (handshakeCompleted) { 431 notifyHandshakeCompletedListeners(); 432 } 433 434 exception = false; 435 } catch (SSLProtocolException e) { 436 throw new SSLHandshakeException(e); 437 } finally { 438 // on exceptional exit, treat the socket as closed 439 if (exception) { 440 close(); 441 } 442 } 443 } 444 445 private String getPeerHostName() { 446 if (wrappedHost != null) { 447 return wrappedHost; 448 } 449 InetAddress inetAddress = super.getInetAddress(); 450 if (inetAddress != null) { 451 return inetAddress.getHostName(); 452 } 453 return null; 454 } 455 456 private int getPeerPort() { 457 return wrappedHost == null ? super.getPort() : wrappedPort; 458 } 459 460 /** 461 * Return a possibly null array of X509Certificates given the 462 * possibly null array of DER encoded bytes. 463 */ 464 private static X509Certificate[] createCertChain(byte[][] certificatesBytes) { 465 if (certificatesBytes == null) { 466 return null; 467 } 468 X509Certificate[] certificates = new X509Certificate[certificatesBytes.length]; 469 for (int i = 0; i < certificatesBytes.length; i++) { 470 try { 471 certificates[i] = new X509CertImpl(certificatesBytes[i]); 472 } catch (IOException e) { 473 return null; 474 } 475 } 476 return certificates; 477 } 478 479 private void setCertificate(String alias) throws CertificateEncodingException, SSLException { 480 if (alias == null) { 481 return; 482 } 483 PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias); 484 if (privateKey == null) { 485 return; 486 } 487 X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias); 488 if (certificates == null) { 489 return; 490 } 491 492 if (privateKey instanceof OpenSSLRSAPrivateKey) { 493 OpenSSLRSAPrivateKey rsaKey = (OpenSSLRSAPrivateKey) privateKey; 494 OpenSSLKey key = rsaKey.getOpenSSLKey(); 495 NativeCrypto.SSL_use_OpenSSL_PrivateKey(sslNativePointer, key.getPkeyContext()); 496 } else if (privateKey instanceof OpenSSLDSAPrivateKey) { 497 OpenSSLDSAPrivateKey dsaKey = (OpenSSLDSAPrivateKey) privateKey; 498 OpenSSLKey key = dsaKey.getOpenSSLKey(); 499 NativeCrypto.SSL_use_OpenSSL_PrivateKey(sslNativePointer, key.getPkeyContext()); 500 } else if ("PKCS#8".equals(privateKey.getFormat())) { 501 byte[] privateKeyBytes = privateKey.getEncoded(); 502 NativeCrypto.SSL_use_PrivateKey(sslNativePointer, privateKeyBytes); 503 } else { 504 throw new SSLException("Unsupported PrivateKey format: " + privateKey.getFormat()); 505 } 506 507 byte[][] certificateBytes = NativeCrypto.encodeCertificates(certificates); 508 NativeCrypto.SSL_use_certificate(sslNativePointer, certificateBytes); 509 510 // checks the last installed private key and certificate, 511 // so need to do this once per loop iteration 512 NativeCrypto.SSL_check_private_key(sslNativePointer); 513 } 514 515 @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / client_cert_cb 516 public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) 517 throws CertificateEncodingException, SSLException { 518 519 String[] keyTypes = new String[keyTypeBytes.length]; 520 for (int i = 0; i < keyTypeBytes.length; i++) { 521 keyTypes[i] = CipherSuite.getClientKeyType(keyTypeBytes[i]); 522 } 523 524 X500Principal[] issuers; 525 if (asn1DerEncodedPrincipals == null) { 526 issuers = null; 527 } else { 528 issuers = new X500Principal[asn1DerEncodedPrincipals.length]; 529 for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) { 530 issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]); 531 } 532 } 533 setCertificate(sslParameters.getKeyManager().chooseClientAlias(keyTypes, issuers, this)); 534 } 535 536 @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / info_callback 537 public void handshakeCompleted() { 538 handshakeCompleted = true; 539 540 // If sslSession is null, the handshake was completed during 541 // the call to NativeCrypto.SSL_do_handshake and not during a 542 // later read operation. That means we do not need to fix up 543 // the SSLSession and session cache or notify 544 // HandshakeCompletedListeners, it will be done in 545 // startHandshake. 546 if (sslSession == null) { 547 return; 548 } 549 550 // reset session id from the native pointer and update the 551 // appropriate cache. 552 sslSession.resetId(); 553 AbstractSessionContext sessionContext = 554 (sslParameters.getUseClientMode()) 555 ? sslParameters.getClientSessionContext() 556 : sslParameters.getServerSessionContext(); 557 sessionContext.putSession(sslSession); 558 559 // let listeners know we are finally done 560 notifyHandshakeCompletedListeners(); 561 } 562 563 private void notifyHandshakeCompletedListeners() { 564 if (listeners != null && !listeners.isEmpty()) { 565 // notify the listeners 566 HandshakeCompletedEvent event = 567 new HandshakeCompletedEvent(this, sslSession); 568 for (HandshakeCompletedListener listener : listeners) { 569 try { 570 listener.handshakeCompleted(event); 571 } catch (RuntimeException e) { 572 // The RI runs the handlers in a separate thread, 573 // which we do not. But we try to preserve their 574 // behavior of logging a problem and not killing 575 // the handshaking thread just because a listener 576 // has a problem. 577 Thread thread = Thread.currentThread(); 578 thread.getUncaughtExceptionHandler().uncaughtException(thread, e); 579 } 580 } 581 } 582 } 583 584 @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks 585 @Override public void verifyCertificateChain(byte[][] bytes, String authMethod) 586 throws CertificateException { 587 try { 588 if (bytes == null || bytes.length == 0) { 589 throw new SSLException("Peer sent no certificate"); 590 } 591 X509Certificate[] peerCertificateChain = new X509Certificate[bytes.length]; 592 for (int i = 0; i < bytes.length; i++) { 593 peerCertificateChain[i] = new X509CertImpl(bytes[i]); 594 } 595 boolean client = sslParameters.getUseClientMode(); 596 if (client) { 597 sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain, 598 authMethod); 599 } else { 600 String authType = peerCertificateChain[0].getPublicKey().getAlgorithm(); 601 sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain, 602 authType); 603 } 604 605 } catch (CertificateException e) { 606 throw e; 607 } catch (RuntimeException e) { 608 throw e; 609 } catch (Exception e) { 610 throw new RuntimeException(e); 611 } 612 } 613 614 @Override public InputStream getInputStream() throws IOException { 615 checkOpen(); 616 synchronized (this) { 617 if (is == null) { 618 is = new SSLInputStream(); 619 } 620 621 return is; 622 } 623 } 624 625 @Override public OutputStream getOutputStream() throws IOException { 626 checkOpen(); 627 synchronized (this) { 628 if (os == null) { 629 os = new SSLOutputStream(); 630 } 631 632 return os; 633 } 634 } 635 636 /** 637 * This inner class provides input data stream functionality 638 * for the OpenSSL native implementation. It is used to 639 * read data received via SSL protocol. 640 */ 641 private class SSLInputStream extends InputStream { 642 SSLInputStream() throws IOException { 643 /* 644 * Note: When startHandshake() throws an exception, no 645 * SSLInputStream object will be created. 646 */ 647 OpenSSLSocketImpl.this.startHandshake(); 648 } 649 650 /** 651 * Reads one byte. If there is no data in the underlying buffer, 652 * this operation can block until the data will be 653 * available. 654 * @return read value. 655 * @throws <code>IOException</code> 656 */ 657 @Override 658 public int read() throws IOException { 659 return Streams.readSingleByte(this); 660 } 661 662 /** 663 * Method acts as described in spec for superclass. 664 * @see java.io.InputStream#read(byte[],int,int) 665 */ 666 @Override 667 public int read(byte[] buf, int offset, int byteCount) throws IOException { 668 BlockGuard.getThreadPolicy().onNetwork(); 669 synchronized (readLock) { 670 checkOpen(); 671 Arrays.checkOffsetAndCount(buf.length, offset, byteCount); 672 if (byteCount == 0) { 673 return 0; 674 } 675 return NativeCrypto.SSL_read(sslNativePointer, socket.getFileDescriptor$(), 676 OpenSSLSocketImpl.this, buf, offset, byteCount, getSoTimeout()); 677 } 678 } 679 } 680 681 /** 682 * This inner class provides output data stream functionality 683 * for the OpenSSL native implementation. It is used to 684 * write data according to the encryption parameters given in SSL context. 685 */ 686 private class SSLOutputStream extends OutputStream { 687 SSLOutputStream() throws IOException { 688 /* 689 * Note: When startHandshake() throws an exception, no 690 * SSLOutputStream object will be created. 691 */ 692 OpenSSLSocketImpl.this.startHandshake(); 693 } 694 695 /** 696 * Method acts as described in spec for superclass. 697 * @see java.io.OutputStream#write(int) 698 */ 699 @Override 700 public void write(int oneByte) throws IOException { 701 Streams.writeSingleByte(this, oneByte); 702 } 703 704 /** 705 * Method acts as described in spec for superclass. 706 * @see java.io.OutputStream#write(byte[],int,int) 707 */ 708 @Override 709 public void write(byte[] buf, int offset, int byteCount) throws IOException { 710 BlockGuard.getThreadPolicy().onNetwork(); 711 synchronized (writeLock) { 712 checkOpen(); 713 Arrays.checkOffsetAndCount(buf.length, offset, byteCount); 714 if (byteCount == 0) { 715 return; 716 } 717 NativeCrypto.SSL_write(sslNativePointer, socket.getFileDescriptor$(), 718 OpenSSLSocketImpl.this, buf, offset, byteCount); 719 } 720 } 721 } 722 723 724 @Override public SSLSession getSession() { 725 if (sslSession == null) { 726 try { 727 startHandshake(); 728 } catch (IOException e) { 729 // return an invalid session with 730 // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL" 731 return SSLSessionImpl.NULL_SESSION; 732 } 733 } 734 return sslSession; 735 } 736 737 @Override public void addHandshakeCompletedListener( 738 HandshakeCompletedListener listener) { 739 if (listener == null) { 740 throw new IllegalArgumentException("Provided listener is null"); 741 } 742 if (listeners == null) { 743 listeners = new ArrayList<HandshakeCompletedListener>(); 744 } 745 listeners.add(listener); 746 } 747 748 @Override public void removeHandshakeCompletedListener( 749 HandshakeCompletedListener listener) { 750 if (listener == null) { 751 throw new IllegalArgumentException("Provided listener is null"); 752 } 753 if (listeners == null) { 754 throw new IllegalArgumentException( 755 "Provided listener is not registered"); 756 } 757 if (!listeners.remove(listener)) { 758 throw new IllegalArgumentException( 759 "Provided listener is not registered"); 760 } 761 } 762 763 @Override public boolean getEnableSessionCreation() { 764 return sslParameters.getEnableSessionCreation(); 765 } 766 767 @Override public void setEnableSessionCreation(boolean flag) { 768 sslParameters.setEnableSessionCreation(flag); 769 } 770 771 @Override public String[] getSupportedCipherSuites() { 772 return NativeCrypto.getSupportedCipherSuites(); 773 } 774 775 @Override public String[] getEnabledCipherSuites() { 776 return enabledCipherSuites.clone(); 777 } 778 779 @Override public void setEnabledCipherSuites(String[] suites) { 780 enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites); 781 } 782 783 @Override public String[] getSupportedProtocols() { 784 return NativeCrypto.getSupportedProtocols(); 785 } 786 787 @Override public String[] getEnabledProtocols() { 788 return enabledProtocols.clone(); 789 } 790 791 @Override public void setEnabledProtocols(String[] protocols) { 792 enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols); 793 } 794 795 /** 796 * The names of the compression methods that may be used on this SSL 797 * connection. 798 * @return an array of compression methods 799 */ 800 public String[] getSupportedCompressionMethods() { 801 return NativeCrypto.getSupportedCompressionMethods(); 802 } 803 804 /** 805 * The names of the compression methods versions that are in use 806 * on this SSL connection. 807 * 808 * @return an array of compression methods 809 */ 810 public String[] getEnabledCompressionMethods() { 811 return enabledCompressionMethods.clone(); 812 } 813 814 /** 815 * Enables compression methods listed by getSupportedCompressionMethods(). 816 * 817 * @throws IllegalArgumentException when one or more of the names in the 818 * array are not supported, or when the array is null. 819 */ 820 public void setEnabledCompressionMethods(String[] methods) { 821 enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods); 822 } 823 824 /** 825 * This method enables session ticket support. 826 * 827 * @param useSessionTickets True to enable session tickets 828 */ 829 public void setUseSessionTickets(boolean useSessionTickets) { 830 this.useSessionTickets = useSessionTickets; 831 } 832 833 /** 834 * This method enables Server Name Indication 835 * 836 * @param hostname the desired SNI hostname, or null to disable 837 */ 838 public void setHostname(String hostname) { 839 this.hostname = hostname; 840 } 841 842 @Override public boolean getUseClientMode() { 843 return sslParameters.getUseClientMode(); 844 } 845 846 @Override public void setUseClientMode(boolean mode) { 847 if (handshakeStarted) { 848 throw new IllegalArgumentException( 849 "Could not change the mode after the initial handshake has begun."); 850 } 851 sslParameters.setUseClientMode(mode); 852 } 853 854 @Override public boolean getWantClientAuth() { 855 return sslParameters.getWantClientAuth(); 856 } 857 858 @Override public boolean getNeedClientAuth() { 859 return sslParameters.getNeedClientAuth(); 860 } 861 862 @Override public void setNeedClientAuth(boolean need) { 863 sslParameters.setNeedClientAuth(need); 864 } 865 866 @Override public void setWantClientAuth(boolean want) { 867 sslParameters.setWantClientAuth(want); 868 } 869 870 @Override public void sendUrgentData(int data) throws IOException { 871 throw new SocketException("Method sendUrgentData() is not supported."); 872 } 873 874 @Override public void setOOBInline(boolean on) throws SocketException { 875 throw new SocketException("Methods sendUrgentData, setOOBInline are not supported."); 876 } 877 878 @Override public void setSoTimeout(int timeoutMilliseconds) throws SocketException { 879 super.setSoTimeout(timeoutMilliseconds); 880 this.timeoutMilliseconds = timeoutMilliseconds; 881 } 882 883 @Override public int getSoTimeout() throws SocketException { 884 return timeoutMilliseconds; 885 } 886 887 /** 888 * Set the handshake timeout on this socket. This timeout is specified in 889 * milliseconds and will be used only during the handshake process. 890 */ 891 public void setHandshakeTimeout(int timeoutMilliseconds) throws SocketException { 892 this.handshakeTimeoutMilliseconds = timeoutMilliseconds; 893 } 894 895 @Override public void close() throws IOException { 896 // TODO: Close SSL sockets using a background thread so they close gracefully. 897 898 synchronized (handshakeLock) { 899 if (!handshakeStarted) { 900 // prevent further attempts to start handshake 901 handshakeStarted = true; 902 903 synchronized (this) { 904 free(); 905 906 if (socket != this) { 907 if (autoClose && !socket.isClosed()) socket.close(); 908 } else { 909 if (!super.isClosed()) super.close(); 910 } 911 } 912 913 return; 914 } 915 } 916 917 NativeCrypto.SSL_interrupt(sslNativePointer); 918 919 synchronized (this) { 920 synchronized (writeLock) { 921 synchronized (readLock) { 922 923 // Shut down the SSL connection, per se. 924 try { 925 if (handshakeStarted) { 926 BlockGuard.getThreadPolicy().onNetwork(); 927 NativeCrypto.SSL_shutdown(sslNativePointer, socket.getFileDescriptor$(), 928 this); 929 } 930 } catch (IOException ignored) { 931 /* 932 * Note that although close() can throw 933 * IOException, the RI does not throw if there 934 * is problem sending a "close notify" which 935 * can happen if the underlying socket is closed. 936 */ 937 } finally { 938 /* 939 * Even if the above call failed, it is still safe to free 940 * the native structs, and we need to do so lest we leak 941 * memory. 942 */ 943 free(); 944 945 if (socket != this) { 946 if (autoClose && !socket.isClosed()) { 947 socket.close(); 948 } 949 } else { 950 if (!super.isClosed()) { 951 super.close(); 952 } 953 } 954 } 955 } 956 } 957 } 958 } 959 960 private void free() { 961 if (sslNativePointer == 0) { 962 return; 963 } 964 NativeCrypto.SSL_free(sslNativePointer); 965 sslNativePointer = 0; 966 guard.close(); 967 } 968 969 @Override protected void finalize() throws Throwable { 970 try { 971 /* 972 * Just worry about our own state. Notably we do not try and 973 * close anything. The SocketImpl, either our own 974 * PlainSocketImpl, or the Socket we are wrapping, will do 975 * that. This might mean we do not properly SSL_shutdown, but 976 * if you want to do that, properly close the socket yourself. 977 * 978 * The reason why we don't try to SSL_shutdown, is that there 979 * can be a race between finalizers where the PlainSocketImpl 980 * finalizer runs first and closes the socket. However, in the 981 * meanwhile, the underlying file descriptor could be reused 982 * for another purpose. If we call SSL_shutdown, the 983 * underlying socket BIOs still have the old file descriptor 984 * and will write the close notify to some unsuspecting 985 * reader. 986 */ 987 if (guard != null) { 988 guard.warnIfOpen(); 989 } 990 free(); 991 } finally { 992 super.finalize(); 993 } 994 } 995 996 @Override 997 public FileDescriptor getFileDescriptor$() { 998 if (socket == this) { 999 return super.getFileDescriptor$(); 1000 } else { 1001 return socket.getFileDescriptor$(); 1002 } 1003 } 1004 1005 /** 1006 * Returns the protocol agreed upon by client and server, or null if no 1007 * protocol was agreed upon. 1008 */ 1009 public byte[] getNpnSelectedProtocol() { 1010 return NativeCrypto.SSL_get_npn_negotiated_protocol(sslNativePointer); 1011 } 1012 1013 /** 1014 * Sets the list of protocols this peer is interested in. If null no 1015 * protocols will be used. 1016 * 1017 * @param npnProtocols a non-empty array of protocol names. From 1018 * SSL_select_next_proto, "vector of 8-bit, length prefixed byte 1019 * strings. The length byte itself is not included in the length. A byte 1020 * string of length 0 is invalid. No byte string may be truncated.". 1021 */ 1022 public void setNpnProtocols(byte[] npnProtocols) { 1023 if (npnProtocols != null && npnProtocols.length == 0) { 1024 throw new IllegalArgumentException("npnProtocols.length == 0"); 1025 } 1026 this.npnProtocols = npnProtocols; 1027 } 1028 } 1029