I'm trying to serve a simple 'Hello world!' response over HTTPS with self-signed certificate using Undertow embedded server. However I get ERR_SSL_VERSION_OR_CIPHER_MISMATCH on the client (Chrome) and SSLHandshakeException: no cipher suites in common in server logs.
It seems that additionally to KeyManager[] array I need to provide the TLS protocol to use, but I can not found where I should put it. Complete example is avaiable at https://github.com/isopov/undertow-https-test/blob/master/src/main/java/com/sopovs/moradanen/UndertowHttpsTest.java
while at https://github.com/isopov/undertow-https-test/blob/master/src/main/java/com/sopovs/moradanen/JettyHttpsTest.java there is example of similar test using Jetty server (to prove that certificate is good).
This change to code worked for me:
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(getKeyManagers(), null, null);
Undertow.builder().addHttpsListener(10443, "0.0.0.0", sslContext)...
Related
I have a server that is sending an HTTPPost request with the Apache HttpClient in Java. I am trying to send the post with my cert attached for validation and the other side is saying they are not receiving anything SSL information. I inherited this code/server setup with minimal documentation so I don't necessarily know it was set up properly in the first place.
Here is the setup.
AWS EC2 server. Linux
Elastic Load balancer setup with SSL Cert.
a. Verified this is working as intended when I visit the server via port 443
Tomcat7 running API server
Java 1.8.0_251
Apache httpclient-4.5.9
Being that there are ways to attach the certificate to each level of this I don't fully understand where the problem is.
Most of the ways I have found revolve around attaching the SSL to the outgoing request in the httpclient object but have been unsuccessful. They are a variation on the following...
KeyStore identityKeyStore = KeyStore.getInstance("JKS");
identityKeyStore.load(identity_file, CERTPASSWORD.toCharArray());
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(identityKeyStore, CERTPASSWORD.toCharArray()).build(); // load client certificate
sslConnectionSocketFactory = new SSLConnectionSocketFactory(
sslContext,
new String[]{"TLSv1.2", "TLSv1.1"},
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
This has not worked and I have verified the .jks file is properly formatted and verified the password. Although if this works as I test on a windows machine I would assume this would be the best option.
This server only has outgoing requests to a few sources all of which will need to be SSL verified so I don't mind if it is sent on every outgoing https request.
Is there somewhere else in my setup that I should be looking into attaching the certificate?
If there is a Load balancer with SSL configured then the SSL stops at the load balancer, and your client certificate is never making it to the server. Client SSL certificates only get passed to the server when the server has an SSL certificate installed on it, and the load balancer is in TCP passthrough mode.
I'm using a Java SE Jersey client to connect to a HTTPS resource which uses two-way SSL.
My SSLContext looks like this:
private SSLContext getSSLContext() {
SslConfigurator sslConfig = SslConfigurator.newInstance()
.keyStoreFile("src/main/certificates/testcert.p12")
.keyPassword("mypassword");
return sslConfig.createSSLContext();
}
The problem is that the client certificate is never sent.
I get error "Warning: no suitable certificate found - continuing without client authentication" and I've tracked the reason to the fact that the client certificate isn't issued to one of the Cert Authorities listed in the server's CertificateRequest message to the client. I know from testing with cURL that the server will accept the certificate regardless. The endpoint is a public test system.
My question: How do I force-send my client certificate? (i.e. my Java SE client should ignore the fact that the testcert.p12 certificate's issuer is not the list of issuers that the server has said it would accept)
Please, don't point me to answers that are about disabling check of the server's certificate or about using self-signed certificates. The server's certificate is just fine.
UPDATE
It turned out my problem was another one. I debug by setting system property javax.net.debug=all. After examining the resulting output it looked to me as if the keystore was empty, even after doing the above. So no wonder why "no suitable certificate found".
Jersey has this 'clever' SslConfigurator class which is there to help you set up an SSLContext. Perhaps just too clever for me, because I couldn't make it work with the above code. So instead, I now configure my SSLContext like below:
KeyStore ks = KeyStore.getInstance("PKCS12");
FileInputStream fis = new FileInputStream("src/main/certificates/testcert.p12");
ks.load(fis, "mypassword".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "mypassword".toCharArray());
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);
// now use 'sc' in Jersey
This works for me where Jersey's helper class didn't. I fully sympathize with Jersey's idea of a helper class for SSLContext, because JSSE seems overly complex here for such a simple use case. Well, well.
You can't. It would be a TLS protocol violation, and therefore there is no API to support it. The various TLS APIs will only send a client certificate if:
It was requested, and
A client certificate can be found that conforms to what is specified in the CertificateRequest message.
You will have to arrange for your server to trust your client certificate, either by getting it signed by a CA or by exporting it to the server's trusted certificate store, whatever form that takes at the server.
My environment is a Maven Project and Wildfly (8.2.1) as Application Server. What I need is to connect wihin a incoming REST call to a third party server using SOAP. I need SSL Client Authentication; therefore, I have my own KeyStore and TrustStore. I create therefore my own SSLContext and need to let the WebService use this SSLContext.
All looks like this:
// Build SSL context with own KeyManager / TrustManager
SSLContext sc = SSLContext.getInstance("TLS");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
String password = "changeit";
ks.load(getClass().getResourceAsStream("/keystore"), password.toCharArray());
kmf.init(ks, password.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
// Now build webservice client
MyWS_Service service = new MyWS_Service(null, new QName("http://...", "MyWS"));
MyWS port = service.getMyWSSOAP();
BindingProvider bindingProvider = (BindingProvider) port;
// set to use own SSLContext
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", sc.getSocketFactory());
// set endpoint
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://hostname:443/.../...");
// perform request
respObj = port.myRequest(myRequestObj);
If I call this code from a JUnit test, all works fine. It uses JAXWS-RI from the JRE.
If I call this code from Wildfly, i.e. from my incoming REST call, where I finally need to fire this request, it does not work because it does not use the own SSLContext. It uses the default SSLContext, which of course is rejected by the third party SOAP server. What I see is that it does not use JAXWS-RI but Apache CXF as JAXWS implementation. So I do guess that bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", sc.getSocketFactory()); is simply ignored [Why?] and has no effect. (I also tried the property name com.sun.xml.ws.transport.https.client.SSLSocketFactory [without internal] - also with no luck.)
I know that I could use HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()) or even use the JVM parameters javax.net.ssl.trustStore, javax.net.ssl.keyStore (and their corresponding password properties). Since this affects all connections, it is out of discussion to use this solution; however, lets look what hapens, if I use it anyway:
JUnit use case: It also works
Wildfly use case: It seems that JAXWS takes the SSLContext, but there is a SSL exception (alert from Server that CA is unknown). This shows that there is even a difference in how to establish the connection. Why is it working, if the code is executed with JUnit? This proofes that the KeyStore / TrustStore is correctly set up with the correct certificates. Isn't it?
Edit:
There is one more proof, that the problem is the JAXWS implementation Wildfly uses: If I just perform a simple HttpsConnection, it even works with my own KeyStore / TrustStore in Wildfly:
url = new URL("https://hostname:443/.../...");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
System.out.println(Utils.inputStreamToString(con.getInputStream()));
So what is the best to do? -> As the question titles, I would like to try to bring Wildfly to also use JAXWS-RI rather than Apache CXF. But I got it not to work until now. I tried to put the following dependency in the pom:
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>2.2.10</version>
</dependency>
But this gives me the following exception:
java.util.ServiceConfigurationError: javax.xml.ws.spi.Provider: Provider com.sun.xml.ws.spi.ProviderImpl could not be instantiated
at java.util.ServiceLoader.fail(ServiceLoader.java:232) ~[?:1.8.0_92]
What is wrong? How can I bring Wildfly to work the same way, as if the code is executed from the same project but "as a JUnit Test"?
Edit:
If you have a tip how to reach the goal (sending SOAP requests using SSL with client auth on Wildfly 8.2.1) in a different way (provided that it is a clean Java EE solution - i.e. not sending own XML bodies :-) and not with too old framworks like Axis 1), it is also welcome! I do need a solution soon - I am fighting for days already...
OK, finally, I gave up to try to replace the JAX-WS implementation used. I got it to correctly setup Apache CXF.
https://stackoverflow.com/a/37268853/4106030
This solve the problem: https://stackoverflow.com/a/46894256/8190026. Wildfly will use the sun implementation instead of apache.cxf
I think it is difficult. There were some comments somewhere about this. By the way, I think WildFly uses RESTEasy, not CXF
I'm new to SSL and security and I want to build a java client for a web service that uses SSL. the server is properly configured to use two way ssl configuration, but how to build the client..
also if spring has anything for this it will be good..
thanks in advance
You dont have to do nothing special with the client, just use HTTPS on your request instead HTTP.
to get things work you have to create a client keystore and truststore, then define SSLContext with these stores, then instantiate SSLSocketFactory to produce SSLSockets like this:
SSLContext sslcontext = SSLContexts.createDefault();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, SSLConnectionSocketFactory.STRICT_HOSTNAME_VERIFIER);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
you should read this http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html to understand how it works.
also this question how to write java client and server applications that uses mutual ssl authentication between them? may be useful.
This question was also posted at TLS client authentication fails when using SunMSCAPI but has not found an answer.
I have a Java6 application running on a Windows Win2008R2 client that connects to a server requiring client certificates. The Java application is required to use the native Windows stores, i.e. Windows-Root and Windows-My, and employs the SunMSCAPI provider. The Windows certificate management console reports that both the client cert and CA cert that signed it are correct and enabled for all purposes.
Server verification works exactly as expected, but client authentication fails when the client throws a SSLException complaining that the privatekey is not an RSA private key. However, when the Java default provider is used instead, and the same client certificate is taken from a JKS keystore, i.e. SunMSCAPI and the Windows stores are not used, client authentication works just as expected and the SSL connection succeeds.
When executed, the application reports that it's using the SunMSCAPI provider and is able to print the correct client certificate as well as information about its RSAPrivateKey. Tracing indicates that the client exception occurs after the servers 'Hello Done' as its response to the server's certificate request is being prepared.
The relevant code bits for the client keystore portion of this are:
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManagerFactory kFac = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Provider pvdr = new sun.security.mscapi.SunMSCAPI();
Security.insertProviderAt(pvdr,1);
KeyStore kStore = KeyStore.getInstance("Windows-MY",pvdr);
kStore.load(null,null);
kFac.init(kstore,null);
sslContext.init(kFac.getKeyManagers(), <a trust factory>.getTrustManagers(), new java.security.SecureRandom());
SSLSocketFactory sockFactory = SSLSocketFactory(sslContext);
SSLSocket sslSock = (SSLSocket)sockFactory.createSocket(<some destination host>, <some destination port>);
BufferedInputStream bInStr = new BufferedInputStream(sslSock.getInputStream());
bInStr.read(<the read arguments>); <<< exception thrown in here
Any pointers or suggestions will be greatly appreciated!
The stacktrace shown in your link shows the problem to be at RSA's JSSL provider. Especially for this case you should use Sun/Oracle's JSSE provider. It's the default one anyway.
Call it as
SSLContext sslContext = SSLContext.getInstance("TLS", "SunJSSE");