TrustSSLProtocolSocketFactory allows you exercise full control over the
HTTPS server certificates you are going to trust. Instead of relying
on the Certificate Authorities already present in "jre/lib/security/cacerts",
TrustSSLProtocolSocketFactory only trusts the public certificates you provide
to its constructor.
TrustSSLProtocolSocketFactory can be used to create SSL
Socket
s
that accepts self-signed certificates. Unlike EasySSLProtocolSocketFactory,
TrustSSLProtocolSocketFactory can be used in production. This is because
it forces you to pre-install the self-signed certificate you are going to
trust locally.
TrustSSLProtocolSocketFactory can parse both Java Keystore Files (*.jks)
and base64 PEM encoded public certificates (*.pem).
Example of using TrustSSLProtocolSocketFactory
1. First we must find the certificate we want to trust. In this example
we'll use gmail.google.com's certificate.
openssl s_client -showcerts -connect gmail.google.com:443
2. Cut & paste into a "cert.pem" any certificates you are interested in
trusting in accordance with your security policies. In this example I'll
actually use the current "gmail.google.com" certificate (instead of the
Thawte CA certificate that signed the gmail certificate - that would be
too boring) - but it expires on June 7th, 2006, so this example won't be
useful for very long!
Here's what my "cert.pem" file looks like:
-----BEGIN CERTIFICATE-----
MIIDFjCCAn+gAwIBAgIDP3PeMA0GCSqGSIb3DQEBBAUAMEwxCzAJBgNVBAYTAlpB
MSUwIwYDVQQKExxUaGF3dGUgQ29uc3VsdGluZyAoUHR5KSBMdGQuMRYwFAYDVQQD
Ew1UaGF3dGUgU0dDIENBMB4XDTA1MDYwNzIyMTI1N1oXDTA2MDYwNzIyMTI1N1ow
ajELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v
dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBJbmMxGTAXBgNVBAMTEGdtYWls
Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALoRiWYW0hZw
9TSn3s9912syZg1CP2TaC86PU1Ao2qf3pVu7Mx10Wl8W+aKZrQlvrYjTwku4sEh+
9uI+gWnfmCd0OyVcXr1eFOGCYiiyaPv79Wtb0m0d8GuiRSJhYkZGzGlgFViws2vR
BAMCD2fdp7WGJUVGYOO+s52dgAMUHQXxAgMBAAGjgecwgeQwKAYDVR0lBCEwHwYI
KwYBBQUHAwEGCCsGAQUFBwMCBglghkgBhvhCBAEwNgYDVR0fBC8wLTAroCmgJ4Yl
aHR0cDovL2NybC50aGF3dGUuY29tL1RoYXd0ZVNHQ0NBLmNybDByBggrBgEFBQcB
AQRmMGQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnRoYXd0ZS5jb20wPgYIKwYB
BQUHMAKGMmh0dHA6Ly93d3cudGhhd3RlLmNvbS9yZXBvc2l0b3J5L1RoYXd0ZV9T
R0NfQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEEBQADgYEAktM1l1cV
ebi+Uo6fCE/eLnvvY6QbNNCsU5Pi9B5E1BlEUG+AGpgzE2cSPw1N4ZZb+2AWWwjx
H8/IrJ143KZZXM49ri3Z2e491Jj8qitrMauT7/hb16Jw6I02/74/do4TtHu/Eifr
EZCaSOobSHGeufHjlqlC3ehC4Bx4mLexIMk=
-----END CERTIFICATE-----
3. Run "openssl x509" to analyze the certificate more deeply. This helps
us answer questions like "Do we really want to trust it? When does it
expire? What's the value of the CN (Common Name) field?".
"openssl x509" is also super cool, and will impress all your friends,
coworkers, family, and that cute girl at the starbucks. :-)
If you dig through "man x509" you'll find this example. Run it:
openssl x509 -in cert.pem -noout -text
4. Rename "cert.pem" to "gmail.pem" so that step 5 works.
5. Setup the TrustSSLProtocolSocketFactory to trust "gmail.google.com"
for URLS of the form "https-gmail://" - but don't trust anything else
when using "https-gmail://":
TrustSSLProtocolSocketFactory sf = new TrustSSLProtocolSocketFactory( "/path/to/gmail.pem" );
Protocol trustHttps = new Protocol("https-gmail", sf, 443);
Protocol.registerProtocol("https-gmail", trustHttps);
HttpClient client = new HttpClient();
GetMethod httpget = new GetMethod("https-gmail://gmail.google.com/");
client.executeMethod(httpget);
6. Notice that "https-gmail://" cannot connect to "www.wellsfargo.com" -
the server's certificate isn't trusted! It would still work using
regular "https://" because Java would use the "jre/lib/security/cacerts"
file.
httpget = new GetMethod("https-gmail://www.wellsfargo.com/");
client.executeMethod(httpget);
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
7. Of course "https-gmail://" cannot connect to hosts where the CN field
in the certificate doesn't match the hostname. The same is supposed to
be true of regular "https://", but HTTPClient is a bit lenient.
httpget = new GetMethod("https-gmail://gmail.com/");
client.executeMethod(httpget);
javax.net.ssl.SSLException: hostname in certificate didn't match: <gmail.com> != <gmail.google.com>
8. You can use "*.jks" files instead of "*.pem" if you prefer. Use the 2nd constructor
in that case to pass along the JKS password:
new TrustSSLProtocolSocketFactory( "/path/to/gmail.jks", "my_password".toCharArray() );