2525import com .cloudant .http .HttpConnection ;
2626import com .cloudant .http .HttpConnectionRequestInterceptor ;
2727import com .cloudant .http .HttpConnectionResponseInterceptor ;
28+ import com .cloudant .http .internal .interceptors .SSLCustomizerInterceptor ;
29+ import com .cloudant .http .internal .interceptors .UserAgentInterceptor ;
2830import com .cloudant .sync .internal .common .RetriableTask ;
2931import com .cloudant .sync .internal .documentstore .DocumentRevsList ;
3032import com .cloudant .sync .internal .documentstore .MultipartAttachmentWriter ;
3941import java .io .InputStream ;
4042import java .io .InputStreamReader ;
4143import java .lang .reflect .Type ;
44+ import java .net .InetAddress ;
45+ import java .net .Socket ;
4246import java .net .URI ;
4347import java .nio .charset .Charset ;
48+ import java .security .KeyManagementException ;
49+ import java .security .NoSuchAlgorithmException ;
4450import java .util .ArrayList ;
4551import java .util .Arrays ;
4652import java .util .Collection ;
53+ import java .util .Collections ;
4754import java .util .HashMap ;
4855import java .util .List ;
4956import java .util .Map ;
5259import java .util .logging .Level ;
5360import java .util .logging .Logger ;
5461
62+ import javax .net .ssl .SSLContext ;
63+ import javax .net .ssl .SSLSocket ;
64+ import javax .net .ssl .SSLSocketFactory ;
65+
5566public class CouchClient {
5667
68+ public static final List <HttpConnectionRequestInterceptor > DEFAULT_REQUEST_INTERCEPTORS ;
69+
70+ // Set up the defaults
71+ static {
72+ HttpConnectionRequestInterceptor ua_interceptor =
73+ new UserAgentInterceptor (CouchClient .class .getClassLoader (),
74+ "META-INF/com.cloudant.sync.client.properties" );
75+ HttpConnectionRequestInterceptor tlsInterceptor = checkAndGetTlsInterceptor ();
76+ DEFAULT_REQUEST_INTERCEPTORS = (tlsInterceptor != null ) ?
77+ Arrays .asList (ua_interceptor , tlsInterceptor ) :
78+ Collections .singletonList (ua_interceptor );
79+ }
80+
5781 private CouchURIHelper uriHelper ;
5882 private List <HttpConnectionRequestInterceptor > requestInterceptors ;
5983 private List <HttpConnectionResponseInterceptor > responseInterceptors ;
@@ -66,6 +90,8 @@ public CouchClient(URI rootUri,
6690 this .requestInterceptors = new ArrayList <HttpConnectionRequestInterceptor >();
6791 this .responseInterceptors = new ArrayList <HttpConnectionResponseInterceptor >();
6892
93+ this .requestInterceptors .addAll (DEFAULT_REQUEST_INTERCEPTORS );
94+
6995 if (requestInterceptors != null ) {
7096 this .requestInterceptors .addAll (requestInterceptors );
7197 }
@@ -75,6 +101,121 @@ public CouchClient(URI rootUri,
75101 }
76102 }
77103
104+ private static SSLCustomizerInterceptor checkAndGetTlsInterceptor () {
105+ // Some assistance for TLSv1.2 support. Two things we check before we try to force TLSv1.2
106+ // so that we don't interfere with any other custom configuration that may have been
107+ // provided:
108+ // i) are we running on Android, and an old version of Android (api level < 20 does not
109+ // have TLSv1.2 enabled by default)
110+ // ii) does the default SSLContext have TLSv1.2 enabled or available
111+ if (Misc .isRunningOnAndroid ()) {
112+ // This block catches all exceptions from TLSv1.2 checks, if we get exceptions we bail
113+ // with a RuntimeException
114+ try {
115+ // Get the API level reflectively so we don't need to import classes only available
116+ // in Android
117+ int androidApiLevel = Class .forName ("android.os.Build$VERSION" ).getField
118+ ("SDK_INT" ).getInt (null );
119+ // If we are on an old version of Android and TLSv1.2 has not been enabled already
120+ // on the default context then we add a special interceptor
121+ if (androidApiLevel < 20 ) {
122+ // Check i has passed we are on an old version of Android
123+
124+ // Get the default SSLContext
125+ SSLContext defSslCtx = SSLContext .getDefault ();
126+
127+ // Check the default protocols for TLSv1.2
128+ if (!Arrays .asList (defSslCtx .getDefaultSSLParameters ().getProtocols ())
129+ .contains (TlsOnlySslSocketFactory .TLSv12 ) &&
130+ Arrays .asList (defSslCtx .getSupportedSSLParameters ().getProtocols ())
131+ .contains (TlsOnlySslSocketFactory .TLSv12 )) {
132+ // Check ii has passed TLSv1.2 is not already enabled on the default context
133+ // but is supported on the default context so we can use it
134+
135+ // In an ideal world we would also check the default SSLSocketFactory that
136+ // is set on the HttpsUrlConnection to see if that has been customized from
137+ // the default, but there isn't really a way to check this, equals is not
138+ // overridden on the SSLSocketFactory and there is no route back to the
139+ // SSLContext that generated the SSLSocketFactory.
140+ SSLContext sc = SSLContext .getInstance (TlsOnlySslSocketFactory .TLSv12 );
141+ sc .init (null , null , null );
142+ // We add this interceptor, but it could be overridden by a later user
143+ // provided interceptor, so this shouldn't change anyone's existing
144+ // customizations.
145+ SSLSocketFactory s = sc .getSocketFactory ();
146+ return new SSLCustomizerInterceptor (new TlsOnlySslSocketFactory (s ));
147+ }
148+ }
149+ } catch (NoSuchAlgorithmException e ) {
150+ throw new RuntimeException (e );
151+ } catch (NoSuchFieldException e ) {
152+ throw new RuntimeException (e );
153+ } catch (IllegalAccessException e ) {
154+ throw new RuntimeException (e );
155+ } catch (ClassNotFoundException e ) {
156+ throw new RuntimeException (e );
157+ } catch (KeyManagementException e ) {
158+ throw new RuntimeException (e );
159+ }
160+ }
161+ return null ;
162+ }
163+
164+ private static final class TlsOnlySslSocketFactory extends SSLSocketFactory {
165+
166+ private static final String TLSv12 = "TLSv1.2" ;
167+ private static final String [] TLSv12Only = new String []{TLSv12 };
168+ private final SSLSocketFactory delegate ;
169+
170+ private TlsOnlySslSocketFactory (SSLSocketFactory delegate ) {
171+ this .delegate = delegate ;
172+ }
173+
174+ @ Override
175+ public String [] getDefaultCipherSuites () {
176+ return delegate .getDefaultCipherSuites ();
177+ }
178+
179+ @ Override
180+ public String [] getSupportedCipherSuites () {
181+ return delegate .getSupportedCipherSuites ();
182+ }
183+
184+ @ Override
185+ public Socket createSocket (Socket socket , String s , int i , boolean b ) throws IOException {
186+ return tlsOnlySocket (delegate .createSocket (socket , s , i , b ));
187+ }
188+
189+ @ Override
190+ public Socket createSocket (String s , int i ) throws IOException {
191+ return tlsOnlySocket (delegate .createSocket (s , i ));
192+ }
193+
194+ @ Override
195+ public Socket createSocket (String s , int i , InetAddress inetAddress , int i1 ) throws
196+ IOException {
197+ return tlsOnlySocket (delegate .createSocket (s , i , inetAddress , i1 ));
198+ }
199+
200+ @ Override
201+ public Socket createSocket (InetAddress inetAddress , int i ) throws IOException {
202+ return tlsOnlySocket (delegate .createSocket (inetAddress , i ));
203+ }
204+
205+ @ Override
206+ public Socket createSocket (InetAddress inetAddress , int i , InetAddress inetAddress1 , int
207+ i1 ) throws IOException {
208+ return tlsOnlySocket (delegate .createSocket (inetAddress , i , inetAddress1 , i1 ));
209+ }
210+
211+ private Socket tlsOnlySocket (Socket s ) {
212+ if (s instanceof SSLSocket ) {
213+ ((SSLSocket ) s ).setEnabledProtocols (TLSv12Only );
214+ }
215+ return s ;
216+ }
217+ }
218+
78219 public URI getRootUri () {
79220 return this .uriHelper .getRootUri ();
80221 }
0 commit comments