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