How to use SSL Client certificate with Apache commons http client - java

My application is using Apache Commons HTTP Client to consume HTTP service URL. Now we have to move over HTTPS endpoint URL. To consume the same, we received SSL Client Certificate. How we can use .JKS with password while consuming HTTPS URL ? (Due to application limitations cant use other APIs)
KeyStore identityKeyStore = KeyStore.getInstance("JKS");
FileInputStream identityKeyStoreFile = new FileInputStream(new File(certificatePath));
identityKeyStore.load(identityKeyStoreFile, password.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(identityKeyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(identityKeyStore, password.toCharArray());
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), null);
SSLContext.setDefault(sslContext);
PostMethod post = new PostMethod("https://url");
HttpClient httpClient = new HttpClient();
String reqMessage = getSolaceRequestMessage(message,hostName,port,authentication);
Part[] parts = {
new StringPart("reqMessage", message),
};
post.setRequestEntity(
new MultipartRequestEntity(parts, post.getParams())
);
httpClient.executeMethod(post);

The *.jks we use in the back service part.
I can give you a example of my project Java Spring boot, I change http --> https in my back service and I added my certificate in Nginx.
Example of https simple services
When you changed back service you can call https directly in your front application(ex.web angular).

I used below implementation which worked for me as had limitation not to upgrade the http client libraries.
System.setProperty(JAVAX_NET_SSL_TRUSTSTORE, "H://certificateFile.jks");
System.setProperty(JAVAX_NET_SSL_TRUSTSTORE_KEY, "abcd");

Related

How to prevent root device to bypass certificate pinning in Android?

I am developing a project that require the Android app can prevent bypassing certificate pinning/trust a fake cert when doing network calling even in a rooted devices.
So far I can make it when the device is not rooted. I just need to prevent some bypassing method like using JustTrustMe in Xposed framework.
I am using retrofit and okHttp during network calling.
I have tried the using CertPinner in okHttp and its version is 3.10.0
and also tried to follow the code in android developer https://developer.android.com/training/articles/security-ssl#java
here is the sample code i have tried and copied from google
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
And the cert pinning sample code
String hostname = "publicobject.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
client.newCall(request).execute();
Both are the simplest code but none of it working
I would like to make it at least prevent some bypassing method like using JustTrustMe in Xposed framework/some easy automated bypassing method.
May i know if it is possible to do it or not, I have also tried some libs like
https://github.com/moxie0/AndroidPinning
suggested by JustTrustMe
https://github.com/Fuzion24/JustTrustMe
After some testing, load CAs from an InputStream would not work for all rooted devices with bypassing module enabled. It still works for normal device
The only way I could prevent it is to use public key cert pinning with proguard at the same time, hope this only help some ppl encounter the same problems.

How do you configure the Apache httpcore5 java HttpAsyncServer to use TLS on its listening port?

When using the apache httpcore5 java library to implement an HTTP server I can not find the proper idiom for rigging the socket to use TLS.
Under the older httpcore version 4.x I could
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(keystoreStream, keystorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyPassword.toCharArray());
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc. init(kmf.getKeyManagers(), null, new SecureRandom());
ServerSocket sSock = sc.getServerSocketFactory().createServerSocket(port);
and then accept connections from that server socket and bind() those sockets to a DefaultHttpServerConnection and handleRequest() in a loop.
I have not yet found an example of how to write a TLS-capable server using the httpcore5 framework.
After some rummaging around I found H2ServerBootstrap.setTlsStrategy() and used it like this:
SSLContext ctx = buildAppSSLContext();
bootstrap.setTlsStrategy(new BasicServerTlsStrategy(ctx, new FixedPortStrategy(new int[]{port})));

Why spark doesn't pick certificate?

I have a Java component for spark and it consumes soap service by building SSL with JKS certificate. I tested the component in local and I get the response from the soap service.
service = new Token_Service();
port = service.getPort(TokenLookupService.class);
BindingProvider bindingProvider = (BindingProvider) port;
bindingProvider.getRequestContext().put(
BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
jsonSpec.getEndpoint());
System.setProperty("https.protocols", "TLSv1.2");
System.setProperty("javax.net.ssl.keyStore","/path/to/my.jks");
System.setProperty("javax.net.ssl.keyStorePassword","mypswd");
System.setProperty("javax.net.ssl.keyStoreType","JKS");
// call the service
List<PANType> panFromToken = port.getPANFromToken(tokens);
This works fine when I debug/step through the code. But when I run spark-submit command, the certificate is not picked up and I get 403: Forbidden error. I changed the the code to build SSL context manually (given below). It worked fine locally/debug-mode but during run-time I got null response (no 403).
SSLContext sc = SSLContext.getInstance("TLSv1.2");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
String certPath = "/path/to/my.jks";
String certPass = "myPassword";
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream(certPath), certPass.toCharArray());
kmf.init(ks, certPass.toCharArray());
sc.init(kmf.getKeyManagers(), null, null);
((BindingProvider) port).getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", sc.getSocketFactory());
I tried running spark with -- conf option but got invalid argument error.
--conf "-Djavax.net.ssl.keyStore=/path/to/my.jks" --conf "-Djavax.net.ssl.keyStorePassword=myPass"
I noticed that System.setProperty() was not working under spark-context. I was testing locally and not on any cluster but spark executor should have picked up the system properties. Nevertheless, setting up the SSL Socket Factory worked.

Setting a client certificate as a request property in a Java HTTP connection?

I have a Java application that connects to another Java app through a socket with SSL, so my client JVM already has the -Djavax.net.ssl.keyStore and -Djavax.net.ssl.trustStore properties set.
This application needs to make some HTTP requests to a web server that requires client authentication. I can open the connection by using a URLConnection in Java which returns an HTTPSURLConnectionImpl.
The client certificate I want to present to the web server in the request is different than the one set as my JVM system property. Is there a way I can set a client cert. as a request property in the HTTPSURLConnectionImpl ?
Setting a SSL "client certificate" is not adequate directly through HTTPSURLConnectionImpl's request properties, because a digital signature is also required to prove you own the certificate. SSL already does all that automatically, so to makes sense to use that layer.
You have two ways to solve your issue going forward.
Through configuration
You can add you client key and certificate to your JVM KeyStore, it should be picked up at Runtime when the server asks for your client-side SSL authentication. (SSL/TLS is designed for that : the server will ask for a client certificate that is signed by it's trusted authority, which allows the SSL Engine to choose the right certificate, even when your KeyStore holds many).
Through Code
You can roll you own SSLContext using custom made KeyStore/TrustStores.
This is a bit complex (I won't elaborate on how to build Keystore instances in Java), but the gist of it is here :
public static void main(String[] args) throws Exception {
KeyStore clientKeyStore = ... // Whatever
KeyStore clientTrustStore = ... // Whatever you need to load
// We build the KeyManager (SSL client credentials we can send)
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(clientKeyStore, "password".toCharArray());
KeyManager[] km = keyFactory.getKeyManagers();
// We build the TrustManager (Server certificates we trust)
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(clientTrustStore);
TrustManager[] tm = trustFactory.getTrustManagers();
// We build a SSLContext with both our trust/key managers
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(km, tm, null);
SSLSocketFactory sslSf = sslContext.getSocketFactory();
// We prepare a URLConnection
URL url = new URL("https://www.google.com");
URLConnection urlConnection = url.openConnection();
// Before actually opening the sockets, we affect the SSLSocketFactory
HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
httpsUrlConnection.setSSLSocketFactory(sslSf);
// Ready to go !
}

ActiveMQ - create embedded broker listening on SSL (TLS) transport error

I would like to create an embedded ActiveMQ broker that listens on SSL protocol using client authentication mechanism (TLS).
Here's my code that expects to do so :
//loading keystore from file
KeyStore keystore = KeyStore.getInstance("pkcs12");
File ksfile = new File("/home/me/client1.pkcs12");
FileInputStream ksfis = new FileInputStream(ksfile);
keystore.load(ksfis, "password".toCharArray());
//loading truststore from file
KeyStore truststore = KeyStore.getInstance("jks");
truststore.load(new FileInputStream(new File("/home/me/client1.truststore")), "password"
.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
kmf.init(keystore, "password".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(truststore);
//broker definition
String cfURI = "ssl://localhost:2032";
BrokerService brokerService = new BrokerService();
brokerService.addConnector(cfURI);
//configure ssl context for the broker
SslContext sslContext = new SslContext(kmf.getKeyManagers(),tmf.getTrustManagers(), null);
//need client authentication
sslContext.getSSLContext().getDefaultSSLParameters().setNeedClientAuth(true);
sslContext.getSSLContext().getDefaultSSLParameters().setWantClientAuth(true);
brokerService.setSslContext(sslContext);
brokerService.start();
When i execute the previous code in a main program, i get the following error :
GRAVE: Could not accept connection : javax.net.ssl.SSLException: No available certificate or key corresponds to the SSL cipher suites which are enabled.
Any suggestions are wellcome !
Thanks for reading.
Has your client set the certificate from the broker in its truststore? I'm afraid thats the problem you are running into.
Other than that, it would probably help if you paste the client code as well
I got this error by using ActiveMQConnectionFactory instead of ActiveMQSslConnectionFactory when connecting from the client

Categories