Skip to content

Commit d2a157a

Browse files
author
Karl Rieb
committed
Enable certificate pinning for OkHttpRequestor and OkHttp3Requestor.
By default, we do not perform certificate pinning in the OkHttp requestors. Enable pinning for the default instances. Fixes T98988
1 parent 2c07c52 commit d2a157a

5 files changed

Lines changed: 86 additions & 11 deletions

File tree

examples/android/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@ dependencies {
6262
compile 'com.android.support:design:23.1.1'
6363
compile 'com.android.support:recyclerview-v7:23.1.1'
6464
compile 'com.fasterxml.jackson.core:jackson-core:2.7.4'
65-
// picasso 2.5.2 doesn't have OkHttp3 support (although it exists on master). Must use OkHttp v2
66-
// until new picasso release
65+
// picasso 2.5.2 doesn't have OkHttp3 support (although it exists
66+
// on master). Must use OkHttp v2 and v3 until new picasso release
6767
compile 'com.squareup.picasso:picasso:2.5.2'
6868
compile 'com.squareup.okhttp:okhttp:2.7.5'
69+
compile 'com.squareup.okhttp3:okhttp:3.3.1'
6970
}
7071

7172
apply plugin: 'com.getkeepsafe.dexcount'

examples/android/src/main/java/com/dropbox/core/examples/android/DropboxClientFactory.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package com.dropbox.core.examples.android;
22

3-
import com.dropbox.core.v2.DbxClientV2;
43
import com.dropbox.core.DbxHost;
54
import com.dropbox.core.DbxRequestConfig;
5+
import com.dropbox.core.http.OkHttp3Requestor;
6+
import com.dropbox.core.v2.DbxClientV2;
67

78
/**
89
* Singleton instance of {@link DbxClientV2} and friends
@@ -14,8 +15,7 @@ public class DropboxClientFactory {
1415
public static void init(String accessToken) {
1516
if (sDbxClient == null) {
1617
DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder("examples-v2-demo")
17-
// don't use OkHttpRequestor so we can verify cert pinning on Android works (since
18-
// Dalvik implementation is slightly different)
18+
.withHttpRequestor(OkHttp3Requestor.INSTANCE)
1919
.build();
2020

2121
sDbxClient = new DbxClientV2(requestConfig, accessToken);

src/main/java/com/dropbox/core/http/OkHttp3Requestor.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,41 @@ private static OkHttpClient defaultOkHttpClient() {
4141
.connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
4242
.readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
4343
.writeTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
44+
// enables certificate pinning
45+
.sslSocketFactory(SSLConfig.getSSLSocketFactory(), SSLConfig.getTrustManager())
4446
.build();
4547
}
4648

49+
/**
50+
* Creates a new instance of this requestor that uses {@code client} for its requests.
51+
*
52+
* <p> NOTE: This constructor will not enable certificate pinning on the client. If you want
53+
* certificate pinning, use the default instance, {@link #INSTANCE}, or clone the default client
54+
* and modify it accordingly:
55+
*
56+
* <pre>
57+
* OkHttpClient client = OkHttpRequestor.INSTANCE.getClient()
58+
* .readTimeout(2, TimeUnit.MINUTES)
59+
* // ... other modifications
60+
* .build();
61+
* HttpRequestor requestor = new OkHttpRequestor(client);
62+
* </pre>
63+
*
64+
* @param client {@code OkHttpClient} to use for requests, never {@code null}
65+
*/
4766
public OkHttp3Requestor(OkHttpClient client) {
67+
if (client == null) throw new NullPointerException("client");
4868
this.client = client;
4969
}
5070

71+
/**
72+
* Returns the underlying {@code OkHttpClient} used to make requests.
73+
*
74+
* If you want to modify the client for a particular request, create a new instance of this
75+
* requestor with the modified client.
76+
*
77+
* @return underlying {@code OkHttpClient} used by this requestor.
78+
*/
5179
public OkHttpClient getClient() {
5280
return client;
5381
}

src/main/java/com/dropbox/core/http/OkHttpRequestor.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,42 @@ private static OkHttpClient defaultOkHttpClient() {
4343
client.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
4444
client.setReadTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
4545
client.setWriteTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
46+
// enables certificate pinning
47+
client.setSslSocketFactory(SSLConfig.getSSLSocketFactory());
4648
return client;
4749
}
4850

51+
/**
52+
* Creates a new instance of this requestor that uses {@code client} for its requests.
53+
*
54+
* <p> The {@code OkHttpClient} will be cloned to prevent further modification.
55+
*
56+
* <p> NOTE: This constructor will not enable certificate pinning on the client. If you want
57+
* certificate pinning, use the default instance, {@link #INSTANCE}, or clone the default client
58+
* and modify it accordingly:
59+
*
60+
* <pre>
61+
* OkHttpClient client = OkHttpRequestor.INSTANCE.getClient(); // returns a clone
62+
* client.setReadTimeout(2, TimeUnit.MINUTES);
63+
* // ... other modifications
64+
* HttpRequestor requestor = new OkHttpRequestor(client);
65+
* </pre>
66+
*
67+
* @param client {@code OkHttpClient} to use for requests (will be cloned), never {@code null}
68+
*/
4969
public OkHttpRequestor(OkHttpClient client) {
50-
this.client = client;
70+
if (client == null) throw new NullPointerException("client");
71+
this.client = client.clone();
5172
}
5273

74+
/**
75+
* Returns a clone of the underlying {@code OkHttpClient} used to make requests.
76+
*
77+
* If you want to modify the client for a particular request, create a new instance of this
78+
* requestor with the modified client.
79+
*
80+
* @return clone of the underlying {@code OkHttpClient} used by this requestor.
81+
*/
5382
public OkHttpClient getClient() {
5483
return client;
5584
}

src/main/java/com/dropbox/core/http/SSLConfig.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import javax.net.ssl.SSLSocketFactory;
3030
import javax.net.ssl.TrustManager;
3131
import javax.net.ssl.TrustManagerFactory;
32+
import javax.net.ssl.X509TrustManager;
3233

3334
/*>>> import checkers.nullness.quals.Nullable; */
3435
/*>>> import checkers.nullness.quals.MonotonicNonNull; */
@@ -55,6 +56,7 @@
5556
*
5657
*/
5758
public class SSLConfig {
59+
private static final X509TrustManager TRUST_MANAGER = createTrustManager();
5860
private static final SSLSocketFactory SSL_SOCKET_FACTORY = createSSLSocketFactory();
5961

6062
private static final String[] PROTOCOL_LIST_TLS_V1_2 = {"TLSv1.2"};
@@ -120,6 +122,10 @@ public static void apply(HttpsURLConnection conn) throws SSLException {
120122
conn.setSSLSocketFactory(SSL_SOCKET_FACTORY);
121123
}
122124

125+
public static X509TrustManager getTrustManager() {
126+
return TRUST_MANAGER;
127+
}
128+
123129
public static SSLSocketFactory getSSLSocketFactory() {
124130
return SSL_SOCKET_FACTORY;
125131
}
@@ -191,10 +197,13 @@ public CipherSuiteFilterationResults(String[] supported, String[] enabled) {
191197
}
192198
}
193199

194-
private static SSLSocketFactory createSSLSocketFactory() {
200+
private static X509TrustManager createTrustManager() {
195201
KeyStore trustedCertKeyStore = loadKeyStore(ROOT_CERTS_RESOURCE);
196-
TrustManager[] trustManagers = createTrustManagers(trustedCertKeyStore);
197-
SSLContext sslContext = createSSLContext(trustManagers);
202+
return createTrustManager(trustedCertKeyStore);
203+
}
204+
205+
private static SSLSocketFactory createSSLSocketFactory() {
206+
SSLContext sslContext = createSSLContext(new TrustManager[] { TRUST_MANAGER });
198207
return new SSLSocketFactoryWrapper(sslContext.getSocketFactory());
199208
}
200209

@@ -270,7 +279,7 @@ private static SSLContext createSSLContext(TrustManager[] trustManagers) {
270279
return sslContext;
271280
}
272281

273-
private static TrustManager[] createTrustManagers(KeyStore trustedCertKeyStore) {
282+
private static X509TrustManager createTrustManager(KeyStore trustedCertKeyStore) {
274283
TrustManagerFactory tmf;
275284
try {
276285
tmf = TrustManagerFactory.getInstance("X509");
@@ -284,7 +293,15 @@ private static TrustManager[] createTrustManagers(KeyStore trustedCertKeyStore)
284293
throw mkAssert("Unable to initialize TrustManagerFactory with key store", ex);
285294
}
286295

287-
return tmf.getTrustManagers();
296+
TrustManager[] trustManagers = tmf.getTrustManagers();
297+
if (trustManagers.length != 1) {
298+
throw new AssertionError("More than 1 TrustManager created.");
299+
}
300+
if (!(trustManagers[0] instanceof X509TrustManager)) {
301+
throw new AssertionError("TrustManager not of type X509: " + trustManagers[0].getClass());
302+
}
303+
304+
return (X509TrustManager) trustManagers[0];
288305
}
289306

290307
private static KeyStore loadKeyStore(String certFileResource) {

0 commit comments

Comments
 (0)