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