I'm working on a legacy application that always used UnboundId over a none SSL connection. Our infrastructure has changed and I need to rework it to SSL. So I changed the code to the following
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null);
FileInputStream fin1 = new FileInputStream("D:/mycert.cer");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
int i = 0;
Certificate cert = cf.generateCertificate(fin1);
trustStore.setCertificateEntry("cert " + i++, cert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustStore.load(null);
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLUtil sslUtil = new SSLUtil(trustManagers);
sslUtil.setDefaultSSLProtocol("TLSv1");
SSLSocketFactory sslServerSocketFactory = sslUtil.createSSLSocketFactory();
LDAPConnection connection = new LDAPConnection(sslServerSocketFactory, server, port, user, password);
This code works. However we are running on a Websphere and all the certificates are located in the Websphere keystore. In this case I downloaded the cert and I'm loading it in from filesystem or resources. This is not what we want. We want to use the keystore of Websphere.
I tried this without defining thrustmanagers and keystores manually, but then I get certificate chaining errors all over the place.
Is there any way to configure UnboundId to use the websphere keystore ?
We had to settle in the end on a semi clean solution. We use the keystore files stored by the websphere server as input to the code.
KeyStore trustStore = KeyStore.getInstance(trustStoreType);
File file = new File(keystoreLocation);
if(file.exists()){
FileInputStream keystoreFile = new FileInputStream(keystoreLocation);
trustStore.load(keystoreFile, keystorePassword.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLUtil sslUtil = new SSLUtil(trustManagers);
sslUtil.setDefaultSSLProtocol(sslProtocol);
SSLSocketFactory sslServerSocketFactory = sslUtil.createSSLSocketFactory();
LDAPConnection connection = new LDAPConnection(sslServerSocketFactory, server, port, user, password);
return connection;
} else {
throw new TechnicalException("Keystore not found");
}
notice keystoreLocation this basically is the keystore file from websphere and kesystorePassword.toCharArray() is the websphere password for that particular keystore. It's not the cleanest of solutions but it got us going again. Maybe this helps others in the future
Related
I was not able to make http client code in .net 5 to send both intermediate and leaf certificates (in 3 certificate hierarchy) to the server. However I was able to send the leaf certificate from client to the server successfully. Here is my setup:
I have 3 certificates on my windows box:
TestRoot.pem
TestIntermediate.pem
TestLeaf.pem (without private key for server - windows box)
TestLeaf.pfx (with private key for client - windows box)
The none of the above certificates were NOT added to windows certificate manager as I would like to be able to run the same code on non-windows machines eventually. For my testing, I am running following client and server code on the same windows box.
On my windows box, I have following simple client side code using .net 5:
HttpClientHandler handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
X509Certificate2 leafCert = new X509Certificate2(File.ReadAllBytes(#"C:\Temp\TestLeaf.pfx"), "<password>");
handler.ClientCertificates.Add(leafCert);
HttpClient httpClient = new HttpClient(handler);
StringContent content = new StringContent("{}"); //Test json string
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(MediaTypeNames.Application.Json);
//With local.TestServer.com resolving to localhost in the host file
HttpResponseMessage response = httpClient.PostAsync("https://local.TestServer.com/...", content).Result;
if (response.IsSuccessStatusCode)
{
var responseString = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseString);
}
else
{
Console.WriteLine(x.StatusCode);
Console.WriteLine(x.ReasonPhrase);
}
On same window box, I have following example snippet of server side code using kestrel in .net 5:
services.Configure<KestrelServerOptions>(options =>
{
// Keep track of what certs belong to each port
var certsGroupedByPort = ...;
var certsPerDistinctSslPortMap = ...;
// Listen to each distinct ssl port a cert specifies
foreach (var certsPerDistinctSslPort in certsPerDistinctSslPortMap)
{
options.Listen(IPAddress.Any, certsPerDistinctSslPort.Key, listenOptions =>
{
var httpsConnectionAdapterOptions = new HttpsConnectionAdapterOptions();
httpsConnectionAdapterOptions.ClientCertificateValidation = (clientCertificate, chain, sslPolicyErrors) =>
{
bool trusted = false;
if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors)
{
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
X509Certificate2 certRoot = new X509Certificate2(#"C:\Temp\TestRoot.pem");
X509Certificate2 certIntermdiate = new X509Certificate2(#"C:\Temp\TestIntermediate.pem");
chain.ChainPolicy.CustomTrustStore.Add(certRoot);
chain.ChainPolicy.ExtraStore.Add(certIntermdiate);
trusted = chain.Build(clientCertificate);
}
return trusted;
};
httpsConnectionAdapterOptions.ServerCertificateSelector = (connectionContext, sniName) =>
{
var defaultCert = //Get default cert
return defaultCert;
};
httpsConnectionAdapterOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
httpsConnectionAdapterOptions.SslProtocols = SslProtocols.Tls12;
listenOptions.UseHttps(httpsConnectionAdapterOptions);
});
}
options.Listen(IPAddress.Any, listeningPort);
});
The above code works as expected because the client code sends the leaf certificate to the server and the server code has access to both intermediate as well as root certificates. The server code can successfully rebuild the certificate hierarchy with received leaf certificate and its configured intermediate and root certs for the leaf certificate.
My following attempt to send the intermediate certificate (along with leaf certificate) to the server (so that it can only use the root certificate and incoming leaf and intermediate certificates in the request to build the certificate hierarchy) failed.
Tried to add the intermediate certificate by doing following in my client code:
X509Certificate2 leafCert = new X509Certificate2(File.ReadAllBytes(#"C:\Temp\TestLeaf.pfx"), "");
X509Certificate2(Convert.FromBase64String(File.ReadAllText(#"C:\Temp\TestIntermediate.pem"));
handler.ClientCertificates.Add(leafCert);
handler.ClientCertificates.Add(intermediateCert);
This did not send the intermediate certificate to the server. I verified this with the code block for httpsConnectionAdapterOptions.ClientCertificateValidation on the server side.
Question:
Is there a way to ensure that intermediate certificate is sent by the client (in addition to the leaf cert) to the server?
I am working on a code that connects to slack through a proxy which act as a MITM and replaces slack cert with its own self signed cert. I added proxy's cert into a trust store and configured my RestTemplate to use the trust store:
def sslContext = new SslContextBuilder().withTrustStore(trustStoreResource, trustStorePassword).build()
def proxy = proxyEnabled ? new HttpHost(proxyHost, proxyPort) : null
def httpClient = HttpClients.custom().setProxy(proxy).setSSLContext(sslContext).build()
def result = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient))
That works fine. However, on my local I don't go through the proxy and connect to slack directly. In other words, the httpClient in the above code would be configured with SSLContext but not proxy. I was expecting this to be fine since Slack's cert is signed with a valid root CA but my code fails to verify Slack's cert.
I am assuming this is because my trustore but I am confused as why this is happening. Is it happening because root CAs are not imported in my trustsore? If so, how would I do that without having to maintain the root CAs?
I understand that locally I can refrain from setting up a trust store but I would like to avoid adding branches in the code if possible.
What I finally ended up doing was to use the implementation in https://gist.github.com/JensRantil/9b7fecb3647ecf1e3076 to combine system's default trust store with mine and then used the following class to build my SSL context. It's a shame HttpClient doesn't offer this but there might be a good reason for it.
import org.springframework.core.io.Resource
import javax.net.ssl.KeyManager
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import java.security.KeyStore
class SslContextBuilder {
private KeyManager[] keyManagers = []
private TrustManager[] trustManagers = []
SslContextBuilder withKeyStore(Resource resource, String password) {
def keyStore = KeyStore.getInstance('JKS')
keyStore.load(resource.getInputStream(), password.chars)
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
kmfactory.init(keyStore, password.chars)
KeyManager[] kms = kmfactory.getKeyManagers()
keyManagers += kms ? kms : []
this
}
SslContextBuilder withTrustStore(Resource resource, String password) {
def trustStore = KeyStore.getInstance('JKS')
trustStore.load(resource.getInputStream(), password.chars)
def tss = CompositeX509TrustManager.getTrustManagers(trustStore)
trustManagers += tss ? tss : []
this
}
SSLContext build() {
def sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null)
sslContext
}
}
After upgrade from WildFly-8.2, EJB remote invoke works fine on HTTP but when I set HTTPS remote connector and URL, it fails. I am invoking from a Java client using InitialContext.
Tried adding the server certificate to java truststore and client truststore but not worked. The client shown certificate unknown.
This issue fixed when loaded the keystore from client code before remote EJB call:
KeyManager[] keyManager = keyManagerFactory.getKeyManagers();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
logger.info("Load JSSE truststore ");
KeyStore trustStore = getTrustStore(userId, null);
if (trustStore == null) {
throw new RuntimeException("A truststore is required for SSL");
}
trustManagerFactory.init(trustStore);
TrustManager[] trustManager = trustManagerFactory.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManager, trustManager, null);
SSLContext.setDefault(sslContext);
The following option passed on running java client not taken for EJB remote SSL call:
-Djavax.net.ssl.trustStore=/home/wf/clientw16.truststore
This way of passing truststore was working for WF-8.2.
Is this changed in WF-16 or any other settings needed?
EDIT: Self-signed certificate installed on browser made it work.
I have configured jetty 9 embedded for http on port 8080 and https on port 8443. When I visit localhost:8080 I get my website and everything works, but when I try to access localhost:8443, I get this mess: image of unreadable stuff.
I pretty much mimicked the ManyConnectorsExample in the jetty documentation in order to set up a http connector and a https connector. Here is my code:
File keystoreFile = new File("config/myownkeystore.p12");
if (!keystoreFile.exists()){
throw new FileNotFoundException(keystoreFile.getAbsolutePath());
}
Server server = new Server();
HttpConfiguration http_config = new HttpConfiguration();
http_config.setSecureScheme("https");
http_config.setSecurePort(8443);
http_config.setOutputBufferSize(32768);
ServerConnector http = new ServerConnector(server,
new HttpConnectionFactory(http_config));
http.setPort(8080);
http.setIdleTimeout(30000);
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStoreType("PKCS12");
sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());
sslContextFactory.setKeyStorePassword("myownstorepass");
sslContextFactory.setKeyManagerPassword("myownkeypass");
HttpConfiguration https_config = new HttpConfiguration(http_config);
SecureRequestCustomizer src = new SecureRequestCustomizer();
src.setStsMaxAge(2000);
src.setStsIncludeSubDomains(true);
https_config.addCustomizer(src);
ServerConnector https = new ServerConnector(server,
new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory(https_config));
https.setPort(8443);
https.setIdleTimeout(500000);
// Set the connectors
server.setConnectors(new Connector[] { http, https });
// servlet handler
ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContextHandler.setResourceBase(".");
servletContextHandler.setContextPath("/");
servletContextHandler.addServlet(new ServletHolder(new MainServlet()), "/*");
// set the handlers
HandlerCollection handlerList = new HandlerCollection();
handlerList.setHandlers(new Handler[]{servletContextHandler});
server.setHandler(handlerList);
At first I thought that maybe the keystore type was at fault, since jetty uses jks by default. So I switched the sslcontext to use pkcs12 since the keystore is a .p12 file. That didn't help.
I checked the passwords next but the password for both private key and for the keystore are the same, so that can't be the issue.
At this point, I want to believe its a fault in my code somewhere, but I don't know what is wrong exactly.
I need send a request to URL with custom keystore .jks file and password . Here is the following piece of code . It is not very clean. I need to clean it up :-) I see an exception of ava.lang.NoSuchFieldError. I have no clue what i am missing here :-/
try{
KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keystoreInput = new FileInputStream(KEY_STORE_PATH);
truststore.load(keystoreInput, KEY_STORE_PASSWORD.toCharArray());
System.out.println("Keystore has " + truststore.size() + " keys");
TrustManagerFactory trustManagerFact = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
trustManagerFact.init(truststore);
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = trustManagerFact.getTrustManagers();
KeyManager[] keyManagers = getKeyManagers("jks", new FileInputStream(KEY_STORE_PATH), KEY_STORE_PASSWORD.toString());
sslContext.init(keyManagers, trustManagers, new SecureRandom());
SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext,new StrictHostnameVerifier());
//sslContext.init(keyManagers, trustManagers, new SecureRandom());
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("https", 443, socketFactory));
// This is the default port number only; others are allowed
System.out.println("Trying to get connection");
//DefaultHttpClient httpclient = new DefaultHttpClient();
ClientConnectionManager manager = httpclient.getConnectionManager();
System.out.println("got connection");
manager.getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
HttpGet httpget = new HttpGet(serviceUrl);
// SOAP request send
response = httpclient.execute(httpget);
..............//some more code
}
public static KeyManager[] getKeyManagers(String keyStoreType, InputStream keyStoreFile, String keyStorePassword) throws Exception {
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(keyStoreFile, keyStorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword.toCharArray());
return kmf.getKeyManagers();
}
I see following exception
java.lang.NoSuchFieldError: org/apache/http/protocol/HTTP.DEF_CONTENT_CHARSET
at org.apache.http.impl.client.DefaultHttpClient.setDefaultHttpParams(DefaultHttpClient.java:175)
at org.apache.http.impl.client.DefaultHttpClient.createHttpParams(DefaultHttpClient.java:158)
at org.apache.http.impl.client.AbstractHttpClient.getParams(AbstractHttpClient.java:448)
at org.apache.http.impl.client.AbstractHttpClient.createClientConnectionManager(AbstractHttpClient.java:309)
at org.apache.http.impl.client.AbstractHttpClient.getConnectionManager(AbstractHttpClient.java:466)
at
ClientConnectionManager manager = httpclient.getConnectionManager();
Can you guys tell me what I am missing here ?
This is often the case when using SSL especially when using 3rd party libraries. Basically you have jar file version mismatch. Check that the version of the jar files you are using are correct. Are you using something like BouncyCastle. Check the version of this jar.