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;
}
}
Related
Scenario, I'm dealing with a Webserver that it is a mess (no I do not control this part, I have to play the game, this was coded by one of the biggest software vendors in the world)
By default, the webserver has 2 SSL services, each one of those might have a totally different SSL Certificate
Certificate A
Signature Algorithm: sha1WithRSAEncryption
RSA Key Strength: 1024
Subject: *.dummy.nodomain
Issuer: *.dummy.nodomain
Certificate B
Signature Algorithm: sha1WithRSAEncryption
RSA Key Strength: 2048
Subject: vhcalnplcs_NPL_01
Issuer: root_NPL
Following the examples of this page
public List<String> doPostWithSSL(String direction, String dataToSend, String contentType, boolean OverrideSecurityVerifications) {
try {
URL url = new URL(direction);
List<String> webcontent = new ArrayList();
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Host", getHostByUrl(direction));
conn = new UserAgentsLibrary().getRandomUserAgent(conn);
if (contentType != null) {
conn.setRequestProperty("Content-Type", contentType);
} else {
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
}
conn.setDoOutput(true);
if (OverrideSecurityVerifications) {
try {
TrustManager[] trustAllCerts;
trustAllCerts = new TrustManager[]{new X509TrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
}};
// We want to override the SSL verifications
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAllCerts, null);
SSLSocketFactory factory = ctx.getSocketFactory();
conn.setDefaultSSLSocketFactory(ctx.getSocketFactory());
HostnameVerifier allHostsValid = (String hostname1, SSLSession session) -> true;
conn.setDefaultHostnameVerifier(allHostsValid);
conn.setSSLSocketFactory(factory);
} catch (KeyManagementException kex) {
System.out.println("[+] Error bypassing SSL Security " + kex.getMessage());
} catch (NoSuchAlgorithmException nsex) {
System.out.println("[+] Error forgeting TLS " + nsex.getMessage());
}
}
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(dataToSend);
wr.flush();
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = rd.readLine()) != null) { //todo+=line+"\n";
webcontent.add(line);
}
wr.close();
rd.close();
return webcontent;
} catch (MalformedURLException mex) {
System.out.println("[+] Error: I received a malformed URL");
return null;
} catch (SSLHandshakeException sslex) {
System.out.println("[+] Error: SSL Handshake Error!" + sslex.getMessage());
return null;
} catch (IOException ioex) {
System.out.println("[+] Error: Input/Output Error!" + ioex.getMessage());
return null;
}
}
I was able to make my program work with certificate B (no issue here) but I cannot make it to work with certificate A (I suspect that the * is causing me trouble)
Things to consider
This is a sample code, do not look for irrelevant details ;)
Yes, I know that this code is vulnerable to MITM attacks and the user is being warned
No, I do not want to add the certificates to my keystore!
I'm using pure J2EE code, I do not wish to use anything that it is not standard
I would like to find a solution that will work for Windows, Mac and Linux
Someone had to have this issue in the past, could you lend me a hand?
I was too tired yesterday.
Replaced conn.setDefaultHostnameVerifier(allHostsValid);
by conn.setHostnameVerifier(allHostsValid);
And now even the cert with the wildcard works!
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.
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.)
This is my scenario , i want to connect to ldap usign jndi , i am using custom SSLSOcketfactory which reads the truststore and keystore . The context is created successful but when i try to authenticate using the same credentials it throws an error telling that the authentication method is not supported.
here is my code of the custom ssl socket -
try {
StringBuffer trustStore = new StringBuffer("c:/Temp/certs/TrustStore");
StringBuffer keyStore = new StringBuffer("c:/Temp/certs/keystore.arun");
StringBuffer keyStorePass = new StringBuffer("xxxxx");
StringBuffer keyAlias = new StringBuffer("user");
StringBuffer keyPass = new StringBuffer("XXXX");
TrustManagerFactory tmf =TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
FileInputStream fis = new FileInputStream(trustStore.toString());
KeyStore ks1 = KeyStore.getInstance("jks");
ks1.load(fis, trustStorePass.toString().toCharArray());
fis.close();
tmf.init(ks1);
TrustManager[] tms = tmf.getTrustManagers();
FileInputStream fin = new FileInputStream(keyStore.toString());
KeyStore ks2 = KeyStore.getInstance("jks");
ks2.load(fin, keyStorePass.toString().toCharArray());
fin.close();
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks2, keyStorePass.toString().toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
if (keyAlias != null && keyAlias.length() > 0) {
for (int i = 0; i < kms.length; i++) {
// We can only deal with instances of X509KeyManager
if (kms[i] instanceof X509KeyManager)
kms[i] = new CustomKeyManager(
(X509KeyManager) kms[i], keyAlias.toString());
}
}
SSLContext context = SSLContext.getInstance("TLS");
context.init(kms,tms, null);
ssf = context.getSocketFactory();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static SocketFactory getDefault() {
return new CustomSSLSocketFactory();
}
And the jndi code which uses this CustomSSLSocketFactory is as follows
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldaps://wx64ads01a.vapps.esca.com:636");
env.put(Context.REFERRAL, "follow");
env.put("java.naming.ldap.derefAliases", "always");
env.put("java.naming.ldap.factory.socket","com.eterra.security.authz.dao.CustomSSLSocketFactory" );
try {
ctx = new InitialLdapContext(env, null);
// start ssl session for server authentication
}catch(Exception e ){
System.out.println(e);
}
try{
ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION,
"EXTERNAL");
String path = "CN=domain,DC=casa,DC=com"
String inFilter = "(&(objectClass=*))";
SearchControls sc = new SearchControls();
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> results = null;
results = ctx.search(path, inFilter, sc);
}
My Context is created perfectly but when i try to authenticate and bind to the ldap , i get Invalid Authentication method . ANy help will be appreciated , Struggling with these error over a long time now . Thanks in advance .
Context.SECURITY_AUTHENTICATION, "EXTERNAL"
when i try to authenticate and bind to the ldap , i get Invalid Authentication method
So your LDAP server doesn't support EXTERNAL authentication.
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.