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