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