Android Open Source - hello-pinnedcerts Custom Trust Manager






From Project

Back to project page hello-pinnedcerts.

License

The source code is released under:

Copyright (c) 2014 Ivan Ku?t Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Softwar...

If you think the Android project hello-pinnedcerts listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package co.infinum.https;
/* w  w w.  j a v  a2  s . c o m*/
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.*;
import java.util.Arrays;
import java.util.List;

/**
 * A custom X509TrustManager implementation that trusts a specified server certificate in addition
 * to those that are in the system TrustStore.
 * Also handles an out-of-order certificate chain, as is often produced by Apache's mod_ssl
 */
public class CustomTrustManager implements X509TrustManager {

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

  /**
   * @param trustStore A KeyStore containing the server certificate that should be trusted
   * @throws NoSuchAlgorithmException
   * @throws KeyStoreException
   */
  public CustomTrustManager(KeyStore trustStore) throws NoSuchAlgorithmException, KeyStoreException {
    this.trustStore = trustStore;

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

    originalTrustManagers = originalTrustManagerFactory.getTrustManagers();
  }

  /**
   * No-op. Never invoked by client, only used in server-side implementations
   * @return
   */
  public X509Certificate[] getAcceptedIssuers() {
    return new X509Certificate[0];
  }

  /**
   * No-op. Never invoked by client, only used in server-side implementations
   * @return
   */
  public void checkClientTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
  }


  /**
   * Given the partial or complete certificate chain provided by the peer,
   * build a certificate path to a trusted root and return if it can be validated and is trusted
   * for client SSL authentication based on the authentication type. The authentication type is
   * determined by the actual certificate used. For instance, if RSAPublicKey is used, the authType should be "RSA".
   * Checking is case-sensitive.
   * Defers to the default trust manager first, checks the cert supplied in the ctor if that fails.
   * @param chain the server's certificate chain
   * @param authType the authentication type based on the client certificate
   * @throws java.security.cert.CertificateException
   */
  public void checkServerTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
    try {
      for (TrustManager originalTrustManager : originalTrustManagers) {
        ((X509TrustManager) originalTrustManager).checkServerTrusted(chain, authType);
      }
    } catch(CertificateException originalException) {
      try {
        // Ordering issue?
        X509Certificate[] reorderedChain = reorderCertificateChain(chain);
        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
   * @param chain the certificate chain, possibly with bad ordering
   * @return the re-ordered certificate chain
   */
  private X509Certificate[] reorderCertificateChain(X509Certificate[] chain) {

    X509Certificate[] reorderedChain = new X509Certificate[chain.length];
    List<X509Certificate> certificates = Arrays.asList(chain);

    int position = chain.length - 1;
    X509Certificate rootCert = findRootCert(certificates);
    reorderedChain[position] = rootCert;

    X509Certificate cert = rootCert;
    while((cert = findSignedCert(cert, certificates)) != null && position > 0) {
      reorderedChain[--position] = cert;
    }

    return reorderedChain;
  }

  /**
   * A helper method for certificate re-ordering.
   * Finds the root certificate in a possibly out-of-order certificate chain.
   * @param certificates the certificate change, possibly out-of-order
   * @return the root certificate, if any, that was found in the list of certificates
   */
  private X509Certificate findRootCert(List<X509Certificate> certificates) {
    X509Certificate rootCert = null;

    for(X509Certificate cert : certificates) {
      X509Certificate signer = findSigner(cert, certificates);
      if(signer == null || signer.equals(cert)) { // no signer present, or self-signed
        rootCert = cert;
        break;
      }
    }

    return rootCert;
  }

  /**
   * A helper method for certificate re-ordering.
   * Finds the first certificate in the list of certificates that is signed by the sigingCert.
   */
  private X509Certificate findSignedCert(X509Certificate signingCert, List<X509Certificate> certificates) {
    X509Certificate signed = null;

    for(X509Certificate cert : certificates) {
      Principal signingCertSubjectDN = signingCert.getSubjectDN();
      Principal certIssuerDN = cert.getIssuerDN();
      if(certIssuerDN.equals(signingCertSubjectDN) && !cert.equals(signingCert)) {
        signed = cert;
        break;
      }
    }

    return signed;
  }

  /**
   * A helper method for certificate re-ordering.
   * Finds the certificate in the list of certificates that signed the signedCert.
   */
  private X509Certificate findSigner(X509Certificate signedCert, List<X509Certificate> certificates) {
    X509Certificate signer = null;

    for(X509Certificate cert : certificates) {
      Principal certSubjectDN = cert.getSubjectDN();
      Principal issuerDN = signedCert.getIssuerDN();
      if(certSubjectDN.equals(issuerDN)) {
        signer = cert;
        break;
      }
    }

    return signer;
  }
}




Java Source Code List

co.infinum.https.CustomTrustManager.java
co.infinum.https.HttpClientBuilder.java
co.infinum.https.IgnorantHttpClient.java
co.infinum.https.MainActivity.java
co.infinum.https.RetrofitApacheClientBuilder.java
co.infinum.https.RetrofitClientBuilder.java
co.infinum.https.retrofit.GitHubService.java
co.infinum.https.retrofit.Logger.java
co.infinum.https.retrofit.User.java