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