Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,25 @@ protected void onCreate(Bundle savedInstanceState) {
// OkHttp 2.x
OkHttpClient client =
new OkHttpClient()
.setSSLSocketFactory(TrustKit.getInstance().getSSLSocketFactory(serverHostname));
.setSslSocketFactory(OkHttp2Helper.getSSLSocketFactory());
client.interceptors().add(OkHttp2Helper.getPinningInterceptor());
client.setFollowRedirects(false);

// OkHttp 3.0.x, 3.1.x and 3.2.x
OkHttpClient client =
new OkHttpClient.Builder()
.sslSocketFactory(TrustKit.getInstance().getSSLSocketFactory(serverHostname))
.sslSocketFactory(OkHttp3Helper.getSSLSocketFactory())
.addInterceptor(OkHttp3Helper.getPinningInterceptor())
.followRedirects(false)
.followSslRedirects(false)

// OkHttp 3.3.x and higher
OkHttpClient client =
new OkHttpClient().newBuilder()
.sslSocketFactory(TrustKit.getInstance().getSSLSocketFactory(serverHostname),
TrustKit.getInstance().getTrustManager(serverHostname))
new OkHttpClient.Builder()
.sslSocketFactory(OkHttp3Helper.getSSLSocketFactory(), OkHttp3Helper.getTrustManager())
.addInterceptor(OkHttp3Helper.getPinningInterceptor())
.followRedirects(false)
.followSslRedirects(false)
.build();
}

Expand Down Expand Up @@ -160,6 +167,7 @@ On Android M and earlier devices, TrustKit provides uses its own implementation
* The `<trust-anchors>` setting is only applied when used within the global `<debug-overrides>` tag. Hence, custom trust anchors for specific domains cannot be set.
* Within the `<trust-anchors>` tag, only `<certificate>` tags pointing to a raw certificate file are supported (the `user` or `system` values for the `src` attribute will be ignored).

For consumers of TrustKit's OkHttpHelper solutions, redirects must to be disabled as Pinning will currently only work properly on the initial request and not any redirects

License
-------
Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ ext{
],
google: [
material: '1.0.0'
],
squareup: [
okhttp3: '3.11.0',
okhttp2: '2.4.0'
]
]

Expand Down
2 changes: 2 additions & 0 deletions trustkit/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ dependencies {
implementation "androidx.annotation:annotation:$rootProject.libVersions.androidx.annotation"
implementation "androidx.legacy:legacy-support-v4:$rootProject.libVersions.androidx.legacySupport"
implementation "androidx.preference:preference:$rootProject.libVersions.androidx.preference"
compileOnly "com.squareup.okhttp3:okhttp:$rootProject.libVersions.squareup.okhttp3"
compileOnly "com.squareup.okhttp:okhttp:$rootProject.libVersions.squareup.okhttp2"

androidTestImplementation "junit:junit:$rootProject.libVersions.junit"
androidTestImplementation "androidx.test:runner:$rootProject.libVersions.androidx.test"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.datatheorem.android.trustkit.pinning;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.Request;

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;

@RequiresApi(api = 17)
public class OkHttp2Helper {
private static RootTrustManager trustManager = new RootTrustManager();

/**
* Retrieve an {@code SSLSSocketFactory} that implements SSL pinning validation based on the
* current TrustKit configuration. It can be used with an OkHttpClient to add SSL
* pinning validation to the connections.
*
* <p>
* The {@code SSLSocketFactory} is configured for the current TrustKit configuration and
* will enforce the configuration's pinning policy.
* </p>
*/
@NonNull
public static SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new X509TrustManager[]{trustManager}, null);

return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
throw new IllegalStateException("SSLSocketFactory creation failed");
}
}

/**
* Returns an {@link com.squareup.okhttp.Interceptor} used to parse the hostname of the
* {@link Request} URL and then save the hostname in the {@link RootTrustManager} which will
* later be used for Certificate Pinning.
*/
@NonNull
public static Interceptor getPinningInterceptor() {
return new PinningInterceptor2(trustManager);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.datatheorem.android.trustkit.pinning;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;

import okhttp3.Interceptor;
import okhttp3.Request;

@RequiresApi(api = 17)
public class OkHttp3Helper {
private static RootTrustManager trustManager = new RootTrustManager();

/**
* Retrieve an {@code SSLSSocketFactory} that implements SSL pinning validation based on the
* current TrustKit configuration. It can be used with an OkHttpClient to add SSL
* pinning validation to the connections.
*
* <p>
* The {@code SSLSocketFactory} is configured for the current TrustKit configuration and
* will enforce the configuration's pinning policy.
* </p>
*/
@NonNull
public static SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new X509TrustManager[]{trustManager}, null);

return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
throw new IllegalStateException("SSLSocketFactory creation failed");
}
}

/**
* Returns an {@link okhttp3.Interceptor} used to parse the hostname of the {@link Request} URL
* and then save the hostname in the {@link RootTrustManager} which will later be used for
* Certificate Pinning.
*/
@NonNull
public static Interceptor getPinningInterceptor() {
return new PinningInterceptor(trustManager);
}

/**
* Returns an instance of the {@link RootTrustManager} used for Certificate Pinning.
*/
@NonNull
public static RootTrustManager getTrustManager() {
return trustManager;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.datatheorem.android.trustkit.pinning;

import androidx.annotation.NonNull;

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

/**
* {@link Interceptor} used to parse the hostname of the {@link Request} URL and then save the
* hostname in the {@link RootTrustManager} which will later be used for Certificate Pinning.
*/
public class PinningInterceptor implements Interceptor {
private final RootTrustManager mTrustManager;

public PinningInterceptor(@NonNull RootTrustManager trustManager) {
mTrustManager = trustManager;
}

@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
String serverHostname = request.url().host();

mTrustManager.setServerHostname(serverHostname);
return chain.proceed(request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.datatheorem.android.trustkit.pinning;

import androidx.annotation.NonNull;

import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import java.io.IOException;

/**
* {@link Interceptor} used to parse the hostname of the {@link Request} URL and then save the
* hostname in the {@link RootTrustManager} which will later be used for Certificate Pinning.
*/
public class PinningInterceptor2 implements Interceptor {
private final RootTrustManager mTrustManager;

public PinningInterceptor2(@NonNull RootTrustManager trustManager) {
mTrustManager = trustManager;
}

@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
String serverHostname = request.url().getHost();

mTrustManager.setServerHostname(serverHostname);
return chain.proceed(request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.datatheorem.android.trustkit.pinning;

import android.net.http.X509TrustManagerExtensions;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import com.datatheorem.android.trustkit.TrustKit;
import com.datatheorem.android.trustkit.config.DomainPinningPolicy;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

/**
* {@link X509TrustManager} used for Certificate Pinning.
*
* <p>This trust manager delegates to the appropriate {@link PinningTrustManager} decided by the
* hostname set by the {@link PinningInterceptor}.</p>
*/
@RequiresApi(api = 17)
class RootTrustManager implements X509TrustManager {
private final ThreadLocal<String> mServerHostname = new ThreadLocal<>();

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
TrustKit.getInstance().getTrustManager(mServerHostname.get()).checkClientTrusted(chain, authType);
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
String host = mServerHostname.get();
DomainPinningPolicy serverConfig =
TrustKit.getInstance().getConfiguration().getPolicyForHostname(host);
//This check is needed for compatibility with the Platform default's implementation of
//the Trust Manager. For APIs 24 and greater, the Platform's default TrustManager states
//that it requires usage of the hostname-aware version of checkServerTrusted for app's that
//implement Android's network_security_config file.
if (serverConfig == null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the need for the if/else here? What about TrustKit.getInstance().getTrustManager(host).checkServerTrusted(chain, authType) in all cases?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment to address the need

new X509TrustManagerExtensions(TrustKit.getInstance().getTrustManager(host)).checkServerTrusted(chain, authType, host);
} else {
TrustKit.getInstance().getTrustManager(host).checkServerTrusted(chain, authType);
}
}

@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}

void setServerHostname(@NonNull String serverHostname) {
mServerHostname.set(serverHostname);
}
}