Why spark doesn't pick certificate? - java

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.

Related

Having trouble getting Oracle SSLEngineSimpleDemo.java working

I took the following example and moved code around so it is better simulating an actual client and actual a server where
the server only has access to the trust store file
the client only has access to the client keystore file
At least in TLS1v2, that is how it worked. After I rework the code so there are two SSL contexts(one server side and one client side), it blows up and does not work
javax.net.ssl.SSLHandshakeException: No available authentication scheme
The code I reworked now reads like this
public SSLEngineSimpleDemo() throws Exception {
File baseWorkingDir = FileFactory.getBaseWorkingDir();
File keyStoreFile = FileFactory.newFile(baseWorkingDir, "src/test/resources/client2.keystore");
char[] passphrase = "123456".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(keyStoreFile), passphrase);
clientCtx = SSLContext.getInstance("TLS");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
clientCtx.init(kmf.getKeyManagers(), null, null);
File trustStoreFile = FileFactory.newFile(baseWorkingDir, "src/test/resources/server2.keystore");
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(new FileInputStream(trustStoreFile), passphrase);
serverCtx = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
serverCtx.init(null, tmf.getTrustManagers(), null);
}
I have code like this that works on TLS1v2 so I am not sure why in TLS1v3, this is not working anymore.
What do I have wrong here?
Is my assumption correct in that the trustStoreFile is my private server key?
Is my assumption correct in that the clientStoreFile is my public key?
Is my assumption correct in that the server only needs the private key?
Is my assumption correct in that the client only needs the public key?
Java version: /Library/Java/JavaVirtualMachines/jdk-11.0.5.jdk
OMG, I am an idiot. The issue was my key generation script naming the first thing client2.keystore(Which was the server2.keystore).
once I fix script to generate the private key/public key par into server2.keystore(instead of the mistake of client2.keystore), export, import public key into client2.keystore, it all works.
I should have provided that script :(.
The exception javax.net.ssl.SSLHandshakeException: No available authentication scheme happens when the operating system running your server doesn't support the authentication method the JVM is looking for.
Additionally, TLSv1.3 can be explicitly specified using when instantiating an SSL context.
Change your clientCtx = SSLContext.getInstance("TLS"); to clientCtx = SSLContext.getInstance("TLSv1.3");
and
serverCtx = SSLContext.getInstance("TLS"); to serverCtx = SSLContext.getInstance("TLSv1.3");
Note: SSLContext supports more options such as
SSLv3,TLSv1,TLSv1.1,TLSv1.2

How to use SSL Client certificate with Apache commons http client

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");

How to enable https with org.jboss.resteasy.plugins.server.netty.NettyJaxrsServer?

I have encountered a problem when I try to set up a https server with resteasy-netty4 (http service is OK)
resteasy version 3.0.16.Final
java version 1.8
By searching from stackoverflow and google, I got some solutions, such as
Simple Java Https Server.
So, the original demo is running successful, but unfortunately, it didn't work after integrating with NettyJaxrsServer.
I created a sslcontext as below:
public SSLContext getSSLContext1() throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLS");
// initialise the keystore
char[] password = "password".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("{PARENT_PATH}\\testkey.jks");
ks.load(fis, password);
// setup the key manager factory
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
// setup the trust manager factory
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
// setup the HTTPS context and parameters
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return SSLContext.getDefault();
}
and call org.immortal.hydra.gateway.server.JaxrsGatewayServer#setSSLContext to enable https server.
It was okay to start up, but failed to serve.
Caused by: javax.net.ssl.SSLHandshakeException: no cipher suites in common
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1666)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:304)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:292)
at sun.security.ssl.ServerHandshaker.chooseCipherSuite(ServerHandshaker.java:1036)
at sun.security.ssl.ServerHandshaker.clientHello(ServerHandshaker.java:739)
at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:221)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker$1.run(Handshaker.java:919)
at sun.security.ssl.Handshaker$1.run(Handshaker.java:916)
at java.security.AccessController.doPrivileged(Native Method)
at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1369)
at io.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1124)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1009)
If you have any suggestions to fix this, please let me know.
It looks like you're setting up the SSL context but then returning the default one. Try changing
return SSLContext.getDefault();
to
return sslContext;
Also it's useful to learn how to debug ssl by enabling SSL logging: http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/ReadDebug.html

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