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 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