How can I use different certificates on specific connections?
Create an SSLSocket
factory yourself, and set it on the HttpsURLConnection
before connecting.
...HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();conn.setSSLSocketFactory(sslFactory);conn.setMethod("POST");...
You'll want to create one SSLSocketFactory
and keep it around. Here's a sketch of how to initialize it:
/* Load the keyStore that includes self-signed cert as a "trusted" entry. */KeyStore keyStore = ... TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());tmf.init(keyStore);SSLContext ctx = SSLContext.getInstance("TLS");ctx.init(null, tmf.getTrustManagers(), null);sslFactory = ctx.getSocketFactory();
If you need help creating the key store, please comment.
Here's an example of loading the key store:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());keyStore.load(trustStore, trustStorePassword);trustStore.close();
To create the key store with a PEM format certificate, you can write your own code using CertificateFactory
, or just import it with keytool
from the JDK (keytool won't work for a "key entry", but is just fine for a "trusted entry").
keytool -import -file selfsigned.pem -alias server -keystore server.jks
I read through LOTS of places online to solve this thing. This is the code I wrote to make it work:
ByteArrayInputStream derInputStream = new ByteArrayInputStream(app.certificateString.getBytes());CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream);String alias = "alias";//cert.getSubjectX500Principal().getName();KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());trustStore.load(null);trustStore.setCertificateEntry(alias, cert);KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");kmf.init(trustStore, null);KeyManager[] keyManagers = kmf.getKeyManagers();TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");tmf.init(trustStore);TrustManager[] trustManagers = tmf.getTrustManagers();SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(keyManagers, trustManagers, null);URL url = new URL(someURL);conn = (HttpsURLConnection) url.openConnection();conn.setSSLSocketFactory(sslContext.getSocketFactory());
app.certificateString is a String that contains the Certificate, for example:
static public String certificateString= "-----BEGIN CERTIFICATE-----\n" + "MIIGQTCCBSmgAwIBAgIHBcg1dAivUzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE" + "BhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBE" + ... a bunch of characters... "5126sfeEJMRV4Fl2E5W1gDHoOd6V==\n" + "-----END CERTIFICATE-----";
I have tested that you can put any characters in the certificate string, if it is self signed, as long as you keep the exact structure above. I obtained the certificate string with my laptop's Terminal command line.
If creating a SSLSocketFactory
is not an option, just import the key into the JVM
Retrieve the public key:
$openssl s_client -connect dev-server:443
, then create a file dev-server.pem that looks like-----BEGIN CERTIFICATE----- lklkkkllklklklklllkllklkllklkkkllklklklklllkllklkllklkkkllklk....-----END CERTIFICATE-----
Import the key:
#keytool -import -alias dev-server -keystore $JAVA_HOME/jre/lib/security/cacerts -file dev-server.pem
.Password: changeitRestart JVM