Difference between KeyStore and KeyManager/TrustManager - ssl

What is the difference between using a KeyStore Object for the keystore and truststore; as opposed to using the KeyManager and TrustManager?
Let me explain why I am asking. I am working with RESTEasy and needed to make a REST call over HTTPS with SSL certificates. I needed to augment how RESTEasy created the ClientRequest. Here is what I figured out initially:
public void afterPropertiesSet() throws Exception {
Assert.isTrue(StringUtils.isNotBlank(getKeystoreName()), "Key Store Name is Blank");
Assert.isTrue(StringUtils.isNotBlank(getKeystorePassword()), "Key Store Password is Blank.");
Assert.isTrue(StringUtils.isNotBlank(getKeystorePath()), "Key Store Path is Blank");
Assert.isTrue(StringUtils.isNotBlank(getTruststoreName()), "Trust Store Name is Blank");
Assert.isTrue(StringUtils.isNotBlank(getTruststorePassword()), "Trust Store Password is Blank.");
Assert.isTrue(StringUtils.isNotBlank(getTruststorePath()), "Trust Store Path is Blank");
// Set the keystore and truststore for mutual authentication
createKeystore();
createTruststore();
if (getHttpClient() == null) {
// Initialize HTTP Client
initializeHttpClient();
}
Assert.notNull(getHttpClient(), "HTTP Client is NULL after initialization");
}
public ClientRequest createClientRequest(String uri) throws URISyntaxException {
ClientExecutor clientExecutor = new ApacheHttpClient4Executor(getHttpClient());
ClientRequestFactory fac = new ClientRequestFactory(clientExecutor, new URI(uri));
return fac.createRequest(uri);
}
private void createTruststore() throws KeyStoreException, FileNotFoundException, IOException,
NoSuchAlgorithmException, CertificateException {
String truststoreFilePath = getTruststorePath() + getTruststoreName();
KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream truststoreInput = getClass().getClassLoader().getResourceAsStream(truststoreFilePath);
truststore.load(truststoreInput, getTruststorePassword().toCharArray());
}
private void createKeystore() throws KeyStoreException, FileNotFoundException, IOException,
NoSuchAlgorithmException, CertificateException {
String keystoreFilePath = getKeystorePath() + getKeystoreName();
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keystoreInput = getClass().getClassLoader().getResourceAsStream(keystoreFilePath);
keystore.load(keystoreInput, getKeystorePassword().toCharArray());
}
/**
* Initializes the HTTP Client
*
* #throws KeyStoreException
* #throws NoSuchAlgorithmException
* #throws UnrecoverableKeyException
* #throws KeyManagementException
*/
private void initializeHttpClient() throws KeyManagementException, UnrecoverableKeyException,
NoSuchAlgorithmException, KeyStoreException {
// Register https and http with scheme registry
SchemeRegistry schemeRegistry = new SchemeRegistry();
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(getKeystore(), getKeystorePassword(), getTrustStore());
schemeRegistry.register(new Scheme(HTTP, 80, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(new Scheme(HTTPS, 443, sslSocketFactory));
// Set connection params
HttpConnectionParams.setConnectionTimeout(httpParameters, serviceConnectionTimeout);
HttpConnectionParams.setSoTimeout(httpParameters, readTimeout);
HttpConnectionParams.setStaleCheckingEnabled(httpParameters, true);
// Create Connection Manager
PoolingClientConnectionManager clientManager = new PoolingClientConnectionManager(schemeRegistry);
clientManager.setMaxTotal(maxTotalConnections);
clientManager.setDefaultMaxPerRoute(defaultMaxConnectionsPerHost);
httpClient = new DefaultHttpClient(clientManager, httpParameters);
}
I ran into a problem with the Peer Certificates and kept getting an exception:
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
I then searched around and found articles/blogs about setting up the HttpClient but using TrustManager and KeyManager. I refactored the code to do the following:
public void afterPropertiesSet() throws Exception {
Assert.isTrue(StringUtils.isNotBlank(getKeystoreName()), "Key Store Name is Blank");
Assert.isTrue(StringUtils.isNotBlank(getKeystorePassword()), "Key Store Password is Blank.");
Assert.isTrue(StringUtils.isNotBlank(getKeystorePath()), "Key Store Path is Blank");
Assert.isTrue(StringUtils.isNotBlank(getTruststoreName()), "Trust Store Name is Blank");
Assert.isTrue(StringUtils.isNotBlank(getTruststorePassword()), "Trust Store Password is Blank.");
Assert.isTrue(StringUtils.isNotBlank(getTruststorePath()), "Trust Store Path is Blank");
if (getHttpClient() == null) {
// Initialize HTTP Client
initializeHttpClient();
}
Assert.notNull(getHttpClient(), "HTTP Client is NULL after initialization");
}
public ClientRequest createClientRequest(String uri) throws URISyntaxException {
ClientExecutor clientExecutor = new ApacheHttpClient4Executor(getHttpClient());
ClientRequestFactory fac = new ClientRequestFactory(clientExecutor, new URI(uri));
return fac.createRequest(uri);
}
/**
* Initializes the HTTP Client
*
* #throws KeyStoreException
* #throws NoSuchAlgorithmException
* #throws UnrecoverableKeyException
* #throws KeyManagementException
*/
private void initializeHttpClient() throws Exception {
if (isCheckPeerCertificates()) {
checkPeerCerts();
}
// Create Trust and Key Managers
// Use TrustManager and KeyManager instead of KeyStore
TrustManager[] trustManagers = getTrustManagers(getTruststorePassword());
KeyManager[] keyManagers = getKeyManagers(getKeystorePassword());
// Create SSL Context
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(keyManagers, trustManagers, new SecureRandom());
// Create SSL Factory
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
// Register https and http with scheme registry
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme(HTTP, 80, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(new Scheme(HTTPS, 443, sslSocketFactory));
// Set connection params
HttpConnectionParams.setConnectionTimeout(httpParameters, serviceConnectionTimeout);
HttpConnectionParams.setSoTimeout(httpParameters, readTimeout);
HttpConnectionParams.setStaleCheckingEnabled(httpParameters, true);
// Create Connection Manager
PoolingClientConnectionManager clientManager = new PoolingClientConnectionManager(schemeRegistry);
clientManager.setMaxTotal(maxTotalConnections);
clientManager.setDefaultMaxPerRoute(defaultMaxConnectionsPerHost);
httpClient = new DefaultHttpClient(clientManager, httpParameters);
}
private TrustManager[] getTrustManagers(String trustStorePassword) throws Exception {
String truststoreFilePath = getTruststorePath() + getTruststoreName();
InputStream trustStoreInput = getClass().getClassLoader().getResourceAsStream(truststoreFilePath);
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(trustStoreInput, trustStorePassword.toCharArray());
TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init(trustStore);
return tmfactory.getTrustManagers();
}
private KeyManager[] getKeyManagers(String keyStorePassword) throws Exception {
String keystoreFilePath = getKeystorePath() + getKeystoreName();
InputStream keyStoreInput = getClass().getClassLoader().getResourceAsStream(keystoreFilePath);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(keyStoreInput, keyStorePassword.toCharArray());
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(keyStore, keyStorePassword.toCharArray());
return kmfactory.getKeyManagers();
}
The second code works just fine. So, what is the a difference between the 2 types of usages?

I think this can help you:
Difference between trustStore and keyStore in Java - SSL
First and major difference between trustStore and keyStore is that trustStore is used by TrustManager and keyStore is used by KeyManager class in Java. KeyManager and TrustManager performs different job in Java, TrustManager determines whether remote connection should be trusted or not i.e. whether remote party is who it claims to and KeyManager decides which authentication credentials should be sent to the remote host for authentication during SSL handshake. if you are an SSL Server you will use private key during key exchange algorithm and send certificates corresponding to your public keys to client, this certificate is acquired from keyStore. On SSL client side, if its written in Java, it will use certificates stored in trustStore to verify identity of Server.
Read more: JavaRevisited blog: http://javarevisited.blogspot.com/2012/09/difference-between-truststore-vs-keyStore-Java-SSL.html (Archived here.)

Related

Apache camel - MQTT with SSL

Where can I find the exact configuration setup for MQTT with SSL. The official docs just have it one line as " SSL is supported " but I could not find anything on how to configure it.
I have read a few forums, but I could not make out anything from it.
Some help on this.
P.S : Before you ask me what have I tried. I just made a route with mqtt as component in camel. I have a couple of certificates which I dont how to use it here.
To everyone who is looking for the instructions in the which does not even exists. Here is our you configure the MQTT component with SSL.
MQTT + SSL with Client , CA Certificate and a Key
Route
MQTTEndpoint mqttEndpoint = null;
MQTTComponent mqttComponent = new MQTTComponent();
mqttComponent.setCamelContext( this.getContext()); //Set camel context
mqttEndpoint = (MQTTEndpoint) mqttComponent.createEndpoint("mqtt://mqtt-queue"); //mqtt://<any-name>
mqttEndpoint.getConfiguration().setHost( "ssl://<your-ssl-broker>" );
SSLContext sc = SSLManager
.getSocketFactory("<ca-certificate>.crt", "<trust-certificate>.crt", "<key>.key", <password>);
mqttEndpoint.getConfiguration().setSubscribeTopicNames("<topic>");
mqttEndpoint.getConfiguration().setSslContext( sc );
SSLContext
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PasswordFinder;
import java.io.*;
import java.nio.file.*;
import java.security.*;
import java.security.cert.*;
import javax.net.ssl.*;
public class SSLManager
{
public static SSLContext getSocketFactory (final String caCrtFile, final String crtFile, final String keyFile,
final String password) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
// load CA certificate
PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));
X509Certificate caCert = (X509Certificate)reader.readObject();
reader.close();
// load client certificate
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
X509Certificate cert = (X509Certificate)reader.readObject();
reader.close();
// load client private key
reader = new PEMReader(
new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),
new PasswordFinder() {
#Override
public char[] getPassword() {
return password.toCharArray();
}
}
);
KeyPair key = (KeyPair)reader.readObject();
reader.close();
// CA certificate is used to authenticate server
KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", caCert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(caKs);
// client key and certificates are sent to server so it can authenticate us
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("certificate", cert);
ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password.toCharArray());
// create SSL socket factory
SSLContext context = SSLContext.getInstance("TLSv1.2");
//Create socket factory if required
//context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
//return context.getSocketFactory();
return context;
}
}
Maven Dependency
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.40</version>
</dependency>

Error in connection establishment: net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH

I have installed the SSL/TLS certificate on the server following the instructions provided by Digicert on the below link. https://www.digicert.com/ssl-certificate-installation-java.htm
Also defined the TrustManager but still i am not able to establish the secure connection.
I am getting the connection failed error with reason "Error in connection establishment: net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH"
Below is my code to add SSL support.
private static void addSSLSupport(DefaultIoFilterChainBuilder chain)
throws Exception {
try {
KeyStore keyStore=KeyStore.getInstance("JKS");
char[] passphrase= {'t','e','s','t','s','s','l'};
keyStore.load(new FileInputStream("/home/ec2-user/digicert/mydomain.jks"),passphrase);
Util.logInfo("Key Store loaded");
SSLContext ctx=SSLContext.getInstance("TLS");
TrustManagerFactory trustFactory=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(keyStore);
X509TrustManager defaultTrustManager = (X509TrustManager) trustFactory.getTrustManagers()[0];
ctx.init(null, trustFactory.getTrustManagers(), null);
SslFilter sslFilter = new SslFilter(ctx);
chain.addLast("sslFilter", sslFilter);
Util.logInfo("SSL ON");
}catch(Exception e){
Util.logError(e.toString());
throw e;
}
}
I have got it worked using KeyManager instead of TrustManager while initializing the SSLContext.
Below is the code for your reference.
private static void addSSLSupport(DefaultIoFilterChainBuilder chain)
throws Exception {
try {
KeyStore keyStore=KeyStore.getInstance("JKS");
char[] passphrase= {'t','e','s','t','s','s','l'};
keyStore.load(new FileInputStream("/root/mydomain.jks"),passphrase);
Util.logInfo("Key Store loaded");
KeyManagerFactory kmf = KeyManagerFactory
.getInstance(KEY_MANAGER_FACTORY_ALGORITHM);
kmf.init(keyStore, passphrase);
SSLContext ctx=SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), null, null);
SslFilter sslFilter = new SslFilter(ctx);
chain.addLast("sslFilter", sslFilter);
Util.logInfo("SSL ON");
}catch(Exception e){
Util.logError(e.toString());
throw e;
}
}

Making HTTPS Request and opening SSL socket in JAVA

I am trying to build a login page. For that, I want to open a SSL socket and make a HTTPS request,but I'm getting Unknown Host Exception in the line
SSLSocket skt = (SSLSocket)sslsf.createSocket("https://31.21.18.222/room_info/x.txt" , 443);
Could someone please tell me what I'm doing wrong? Also, I've turned off host verification because it wont be needed in my program.
`public void clickLogin() throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
URL url = new URL ("https://31.21.18.222/room_info/x.txt");
HttpsURLConnection connection = null;
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null); //Make an empty store
InputStream fis = new FileInputStream("C:/Documents and Settings/user/Desktop/PK/localhost.crt");
BufferedInputStream bis = new BufferedInputStream(fis);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
java.security.cert.Certificate cert = cf.generateCertificate(bis);
keyStore.setCertificateEntry("localhost", cert);
}
// write code for turning off client verification
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(keyStore);
SSLContext context = SSLContext.getInstance("SSL");
context.init(null, tmf.getTrustManagers() , null);
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
SSLSocketFactory sslsf = context.getSocketFactory();
SSLSocket skt = (SSLSocket)sslsf.createSocket("https://31.21.18.222/room_info/x.txt" , 443);
skt.setUseClientMode(true);
SSLSession s = skt.getSession(); // handshake implicitly done
skt.setKeepAlive(true);
connection = (HttpsURLConnection) url.openConnection();
// Host name verification off
connection.setHostnameVerifier(new HostnameVerifier()
{
public boolean verify(String hostname, SSLSession session)
{
return true;
}
}); `
If you want to open a socket with createSocket, you need to use the host name (or IP address), not the full URL:
example : sslsf.createSocket("31.21.18.222" , 443);
In addition:
Don't use Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()) (it's there by default).
It's probably better to use TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) instead of X.509, especially because the default algorithm for the TMF is PKIX, not X.509.
createSocket will verify the certificate against the trust anchors, but won't check the host name (which is also required to prevent MITM attacks). For this, it's also generally better to use a host name instead of an IP address.

How can I configure Simple Framework to require SSL client authentication?

I am writing an HTTP server using the Simple Framework and would like to require clients to present a valid certificate signed by my certificate authority in order to establish a connection.
I have the following bare-bones server written. How can I modify this code to require client certificate authentication for all SSL connections?
public static void main(String[] args) throws Exception {
Container container = createContainer();
Server server = new ContainerServer(container);
Connection connection = new SocketConnection(server);
SocketAddress address = new InetSocketAddress(8443);
KeyManagerFactory km = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore serverKeystore = KeyStore.getInstance(KeyStore.getDefaultType());
try(InputStream keystoreFile = new FileInputStream(SERVER_KEYSTORE_PATH)) {
serverKeystore.load(keystoreFile, "asdfgh".toCharArray());
}
km.init(serverKeystore, SERVER_KS_PASSWORD.toCharArray());
TrustManagerFactory tm = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore caKeystore = KeyStore.getInstance(KeyStore.getDefaultType());
try(InputStream caCertFile = new FileInputStream(CA_CERT_PATH)) {
caKeystore.load(caCertFile, CA_KS_PASSWORD.toCharArray());
}
tm.init(caKeystore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(km.getKeyManagers(), tm.getTrustManagers(), null);
connection.connect(address, sslContext);
}
Try this
public class MyServer implements org.simpleframework.transport.Server {
private Server delegateServer;
public MyServer(Server delegateServer){
this.delegateServer = delegateServer;
}
public void process(Socket socket){
socket.getEngine().setNeedClientAuth(true);
delegateServer.process(socket);
}
}

Java SSL - InstallCert recognizes certificate, but still "unable to find valid certification path" error?

Thinking I'd hit the same issue as other folks, I've been going through the numerous similar problems and potential solutions, but with no luck.
The trust store I'm using is cacerts, located in lib/security of a Java 1.6.0 JRE (build 1.6.0_20-b02... could this be the root of the problem?). I've also tried with jssecacerts.
Using InstallCert (per other similar issues posted), I can see my certificate is in fact installed and valid (and I've removed it, re-imported it, etc to make sure I'm seeing the right data):
java InstallCert <my host name>
Loading KeyStore jssecacerts...
Opening connection to <my host name>:443...
Starting SSL handshake...
No errors, certificate is already trusted
Checking in keytool and Portecle, re-importing the cert (I've tried generating from openssl with -showcert, exporting from browsers and scp'ing it over, etc) gives me "That already exists under this other alias over here" type of message. So there doesn't appear to be any issue with the way the cert is getting into the tool(s).
Forcing explicit trustStore paths in the code doesn't make any difference, and in all cases what I end up seeing when I turn on debugging (via a setProperty of javax.net.debug to "all") is:
main, SEND TLSv1 ALERT: fatal, description = certificate_unknown
main, WRITE: TLSv1 Alert, length = 2 [Raw write]: length = 7 0000: 15
03 01 00 02 02 2E ....... main, called
closeSocket() main, handling exception:
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
Unfortunately I can't allow overriding the check by implementing my own TrustManager - it has to actually check.
The certificate I get from the host has a number of extensions (9, to be exact), which makes me wonder if they're somehow part of this issue.
What else can I check/try? Change over to a different JRE version?
You can still check the certificate by implementing your own trust manager. I ran into a similar issue here. I also tried adding the certificate to cacerts but to no avail.
In your trust manager, you need to explicitly load up the certificates. Essentially what I had to do was something like this:
First I create a trust manager that uses the actual certificate files:
public class ValicertX509TrustManager implements X509TrustManager {
X509TrustManager pkixTrustManager;
ValicertX509TrustManager() throws Exception {
String valicertFile = "/certificates/ValicertRSAPublicRootCAv1.cer";
String commwebDRFile = "/certificates/DR_10570.migs.mastercard.com.au.crt";
String commwebPRODFile = "/certificates/PROD_10549.migs.mastercard.com.au.new.crt";
Certificate valicert = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(valicertFile));
Certificate commwebDR = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebDRFile));
Certificate commwebPROD = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebPRODFile));
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, "".toCharArray());
keyStore.setCertificateEntry("valicert", valicert);
keyStore.setCertificateEntry("commwebDR", commwebDR);
keyStore.setCertificateEntry("commwebPROD", commwebPROD);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(keyStore);
TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();
for(TrustManager trustManager : trustManagers) {
if(trustManager instanceof X509TrustManager) {
pkixTrustManager = (X509TrustManager) trustManager;
return;
}
}
throw new Exception("Couldn't initialize");
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
pkixTrustManager.checkServerTrusted(chain, authType);
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
pkixTrustManager.checkServerTrusted(chain, authType);
}
public X509Certificate[] getAcceptedIssuers() {
return pkixTrustManager.getAcceptedIssuers();
}
}
Now, using this trust manager, I had to create a socket factory:
public class ValicertSSLProtocolSocketFactory implements ProtocolSocketFactory {
private SSLContext sslContext = null;
public ValicertSSLProtocolSocketFactory() {
super();
}
private static SSLContext createValicertSSLContext() {
try {
ValicertX509TrustManager valicertX509TrustManager = new ValicertX509TrustManager();
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new ValicertX509TrustManager[] { valicertX509TrustManager}, null);
return context;
}
catch(Exception e) {
Log.error(Log.Context.Net, e);
return null;
}
}
private SSLContext getSSLContext() {
if(this.sslContext == null) {
this.sslContext = createValicertSSLContext();
}
return this.sslContext;
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException {
return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException {
if(params == null) {
throw new IllegalArgumentException("Parameters may not be null");
}
int timeout = params.getConnectionTimeout();
SocketFactory socketFactory = getSSLContext().getSocketFactory();
if(timeout == 0) {
return socketFactory.createSocket(host, port, localAddress, localPort);
}
else {
Socket socket = socketFactory.createSocket();
SocketAddress localAddr = new InetSocketAddress(localAddress, localPort);
SocketAddress remoteAddr = new InetSocketAddress(host, port);
socket.bind(localAddr);
socket.connect(remoteAddr, timeout);
return socket;
}
}
public Socket createSocket(String host, int port) throws IOException {
return getSSLContext().getSocketFactory().createSocket(host, port);
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
public boolean equals(Object obj) {
return ((obj != null) && obj.getClass().equals(ValicertSSLProtocolSocketFactory.class));
}
public int hashCode() {
return ValicertSSLProtocolSocketFactory.class.hashCode();
}
}
Then I just registered a new protocol:
Protocol.registerProtocol("vhttps", new Protocol("vhttps", new ValicertSSLProtocolSocketFactory(), 443));
PostMethod postMethod = new PostMethod(url);
for (Map.Entry<String, String> entry : params.entrySet()) {
postMethod.addParameter(entry.getKey(), StringUtils.Nz(entry.getValue()));
}
HttpClient client = new HttpClient();
int status = client.executeMethod(postMethod);
if (status == 200) {
StringBuilder resultBuffer = new StringBuilder();
resultBuffer.append(postMethod.getResponseBodyAsString());
return new HttpResponse(resultBuffer.toString(), "");
} else {
throw new IOException("Invalid response code: " + status);
}
The only disadvantage is that I had to create a specific protocol (vhttps) for this particular certificate.
The SSL debug trace will show which cacerts file you are using, as long as you don't manually load it yourself. Clearly you aren't using the one you think you are.
My guess is either of these things happened:
a) You run your code on a web server. They often use their own trust store - so are you really sure that it's cacerts that's being used when your code is executed?
b) By default, Java will try to check the validity of the certificates by downloading and interpreting CRLs. If you are behind a proxy, the download fails, and as a consequence the whole PKIX check would fail.