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