Skip to content

Commit

Permalink
Fixed the custom trust manager so it properly uses the given keystore…
Browse files Browse the repository at this point in the history
…. Also added oauth token header to the github retrofit request, so you can test it for more then 60 times a hour.
  • Loading branch information
Wessel van Norel committed Jul 15, 2014
1 parent cfe9093 commit a8f9642
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 25 deletions.
41 changes: 29 additions & 12 deletions pinnedcerts/src/main/java/co/infinum/https/CustomTrustManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
public class CustomTrustManager implements X509TrustManager {

private final X509TrustManager originalX509TrustManager;
private final TrustManager[] originalTrustManagers;
private final KeyStore trustStore;

/**
Expand All @@ -29,11 +29,10 @@ public class CustomTrustManager implements X509TrustManager {
public CustomTrustManager(KeyStore trustStore) throws NoSuchAlgorithmException, KeyStoreException {
this.trustStore = trustStore;

TrustManagerFactory originalTrustManagerFactory = TrustManagerFactory.getInstance("X509");
originalTrustManagerFactory.init((KeyStore) null);
final TrustManagerFactory originalTrustManagerFactory = TrustManagerFactory.getInstance("X509");
originalTrustManagerFactory.init(trustStore);

TrustManager[] originalTrustManagers = originalTrustManagerFactory.getTrustManagers();
originalX509TrustManager = (X509TrustManager) originalTrustManagers[0];
originalTrustManagers = originalTrustManagerFactory.getTrustManagers();
}

/**
Expand Down Expand Up @@ -65,23 +64,41 @@ public void checkClientTrusted(X509Certificate[] chain, String authType) throws
*/
public void checkServerTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
try {
originalX509TrustManager.checkServerTrusted(chain, authType);
for (TrustManager originalTrustManager : originalTrustManagers) {
((X509TrustManager) originalTrustManager).checkServerTrusted(chain, authType);
}
} catch(CertificateException originalException) {
try {
// Ordering issue?
X509Certificate[] reorderedChain = reorderCertificateChain(chain);
CertPathValidator validator = CertPathValidator.getInstance("PKIX");
CertificateFactory factory = CertificateFactory.getInstance("X509");
CertPath certPath = factory.generateCertPath(Arrays.asList(reorderedChain));
PKIXParameters params = new PKIXParameters(trustStore);
params.setRevocationEnabled(false);
validator.validate(certPath, params);
if (! Arrays.equals(chain, reorderedChain)) {
checkServerTrusted(reorderedChain, authType);
return;
}
for (int i = 0; i < chain.length; i++) {
if (validateCert(reorderedChain[i])) {
return;
}
}
throw originalException;
} catch(Exception ex) {
ex.printStackTrace();
throw originalException;
}
}

}

/**
* Checks if we have added the certificate in the trustStore, if that's the case we trust the certificate
* @param x509Certificate the certificate to check
* @return true if we know the certificate, false otherwise
* @throws KeyStoreException on problems accessing the key store
*/
private boolean validateCert(final X509Certificate x509Certificate) throws KeyStoreException {
return trustStore.getCertificateAlias(x509Certificate) != null;
}

/**
* Puts the certificate chain in the proper order, to deal with out-of-order
* certificate chains as are sometimes produced by Apache's mod_ssl
Expand Down
68 changes: 56 additions & 12 deletions pinnedcerts/src/main/java/co/infinum/https/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;

import co.infinum.https.retrofit.GitHubService;
import co.infinum.https.retrofit.Logger;
Expand Down Expand Up @@ -116,7 +118,10 @@ public void onEventAsync(HttpGet request) {

HttpResponse response = httpClient.execute(request);

EventBus.getDefault().post(response);
Map<String, Object> data = new HashMap<String, Object>();
data.put("request", request);
data.put("response", response);
EventBus.getDefault().post(data);
} catch (IOException e) {
e.printStackTrace();
EventBus.getDefault().post(request.getURI().toString()+" "+e.getMessage());
Expand All @@ -135,10 +140,13 @@ public void onEventAsync(HttpGet request) {
/**
* Called after Apache request was completed. Shows status in label.
*
* @param response
* @param data data object with request and response in it
*/
public void onEventMainThread(HttpResponse response) {
statusTextView.setText(response.getStatusLine().toString());
public void onEventMainThread(Map<String, Object> data) {
HttpGet request = (HttpGet) data.get("request");
HttpResponse response = (HttpResponse) data.get("response");

statusTextView.setText("Apache "+request.getURI().toString()+" "+response.getStatusLine().toString());
}

/**
Expand All @@ -147,7 +155,7 @@ public void onEventMainThread(HttpResponse response) {
* @param e
*/
public void onEventMainThread(String e) {
statusTextView.setText(e);
statusTextView.setText("Apache "+e);
}


Expand All @@ -170,16 +178,16 @@ private void makeRetrofitRequest() {

GitHubService service = restAdapter.create(GitHubService.class);

service.getUser(USER, new Callback<User>() {
service.getUser(USER, "token "+this.getString(R.string.oauth_token), new Callback<User>() {

@Override
public void success(User user, Response response) {
statusTextView.setText(response.getStatus() + " " + response.getReason());
statusTextView.setText("Retrofit "+TEST_URL+" "+response.getStatus() + " " + response.getReason());
}

@Override
public void failure(RetrofitError error) {
statusTextView.setText(TEST_URL+ " "+error.getMessage());
statusTextView.setText("Retrofit "+TEST_URL+ " "+error.getMessage());
}
});
} catch (CertificateException e) {
Expand All @@ -201,10 +209,46 @@ public void failure(RetrofitError error) {
* Demonstrates that a request to a host with certificate different than the pinned one will fail.
*/
private void makeForbiddenRequest() {
HttpGet request = new HttpGet(FORBIDDEN_URL);
request.addHeader("User-Agent", "hello-pinnedcerts");

EventBus.getDefault().post(request);
try {
OkClient retrofitClient = new RetrofitClientBuilder()
.setConnectionTimeout(10000)
.pinCertificates(getResources(), R.raw.keystore, STORE_PASS)
.build();

RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(FORBIDDEN_URL)
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(new Logger())
.setClient(retrofitClient)
.build();

GitHubService service = restAdapter.create(GitHubService.class);

service.getUser(USER, "dummy", new Callback<User>() {

@Override
public void success(User user, Response response) {
statusTextView.setText("Retrofit "+FORBIDDEN_URL+" "+response.getStatus() + " " + response.getReason());
}

@Override
public void failure(RetrofitError error) {
statusTextView.setText("Retrofit "+FORBIDDEN_URL+ " "+error.getMessage());
}
});
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import retrofit.Callback;
import retrofit.http.GET;
import retrofit.http.Header;
import retrofit.http.Headers;
import retrofit.http.Path;

Expand All @@ -20,6 +21,7 @@ public interface GitHubService {
@Headers("User-Agent: hello-pinnedcerts")
void getUser(
@Path("user") String user,
@Header("Authorization") String authorizationHeader,
Callback<User> callback

);
Expand Down
3 changes: 2 additions & 1 deletion pinnedcerts/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<string name="apache_request">Apache request</string>
<string name="retrofit_request">Retrofit request</string>
<string name="welcome_label"><![CDATA[Go to menu -> Apache request / Retrofit Request]]></string>
<string name="action_other">Apache - other host</string>
<string name="action_other">Retrofit - other host</string>
<string name="oauth_token">YOUR_TOKEN_HERE</string>

</resources>

0 comments on commit a8f9642

Please sign in to comment.