package com.chariotsolutions.example.http; import android.util.Base64; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import java.io.*; import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; /** * A factory for SSLContexts. * Builds an SSLContext with custom KeyStore and TrustStore, to work with a client cert signed by a self-signed CA cert. */ public class SSLContextFactory { private static SSLContextFactory theInstance = null; private SSLContextFactory() { } public static SSLContextFactory getInstance() { if(theInstance == null) { theInstance = new SSLContextFactory(); } return theInstance; } /** * Creates an SSLContext with the client and server certificates * @param clientCertFile A File containing the client certificate * @param clientCertPassword Password for the client certificate * @param caCertString A String containing the server certificate * @return An initialized SSLContext * @throws Exception */ public SSLContext makeContext(File clientCertFile, String clientCertPassword, String caCertString) throws Exception { final KeyStore keyStore = loadPKCS12KeyStore(clientCertFile, clientCertPassword); KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509"); kmf.init(keyStore, clientCertPassword.toCharArray()); KeyManager[] keyManagers = kmf.getKeyManagers(); final KeyStore trustStore = loadPEMTrustStore(caCertString); TrustManager[] trustManagers = {new CustomTrustManager(trustStore)}; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagers, trustManagers, null); return sslContext; } /** * Produces a KeyStore from a String containing a PEM certificate (typically, the server's CA certificate) * @param certificateString A String containing the PEM-encoded certificate * @return a KeyStore (to be used as a trust store) that contains the certificate * @throws Exception */ private KeyStore loadPEMTrustStore(String certificateString) throws Exception { byte[] der = loadPemCertificate(new ByteArrayInputStream(certificateString.getBytes())); ByteArrayInputStream derInputStream = new ByteArrayInputStream(der); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream); String alias = cert.getSubjectX500Principal().getName(); KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null); trustStore.setCertificateEntry(alias, cert); return trustStore; } /** * Produces a KeyStore from a PKCS12 (.p12) certificate file, typically the client certificate * @param certificateFile A file containing the client certificate * @param clientCertPassword Password for the certificate * @return A KeyStore containing the certificate from the certificateFile * @throws Exception */ private KeyStore loadPKCS12KeyStore(File certificateFile, String clientCertPassword) throws Exception { KeyStore keyStore = null; FileInputStream fis = null; try { keyStore = KeyStore.getInstance("PKCS12"); fis = new FileInputStream(certificateFile); keyStore.load(fis, clientCertPassword.toCharArray()); } finally { try { if(fis != null) { fis.close(); } } catch(IOException ex) { // ignore } } return keyStore; } /** * Reads and decodes a base-64 encoded DER certificate (a .pem certificate), typically the server's CA cert. * @param certificateStream an InputStream from which to read the cert * @return a byte[] containing the decoded certificate * @throws IOException */ byte[] loadPemCertificate(InputStream certificateStream) throws IOException { byte[] der = null; BufferedReader br = null; try { StringBuilder buf = new StringBuilder(); br = new BufferedReader(new InputStreamReader(certificateStream)); String line = br.readLine(); while(line != null) { if(!line.startsWith("--")){ buf.append(line); } line = br.readLine(); } String pem = buf.toString(); der = Base64.decode(pem, Base64.DEFAULT); } finally { if(br != null) { br.close(); } } return der; } }