Comments (12)
You asked couple of question, I will try to answer it
However, some have went to great lengths in order to try to setup it. Here's one of the more popular answers in Stackoverflow. I've yet to test it out fully. Even if does work, the solution raises few questions, like why do we need to SSLEngine engine = c.createSSLEngine(); (I'm aware of SSLEngine in JSSE hierarchy).
You don't need the SSLEngine at all for configuring the SUN HttpsServer. The only object what you need is a SSLContext. In your example/stackoverflow they use it to get the default SSLParameters which contain the protocols and the ciphers. These are optional. If not provided it should fetch it from the SSLContext instance..
The solution would be to add another module, called something like server-with-sun-httpserver and provide implementation with explanation how and why to setup it.
You wanted a basic example, here is a snippet of it.
InetSocketAddress socketAddress = new InetSocketAddress(8443);
SSLFactory sslFactory = ...;
HttpsServer httpsServer = HttpsServer.create(socketAddress, 0);
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslFactory.getSslContext()) {
@Override
public void configure(HttpsParameters params) {
params.setSSLParameters(sslFactory.getSslParameters());
params.setNeedClientAuth(true);
}
});
httpsServer.createContext("/api/hello", new HelloWorldController());
httpsServer.setExecutor(executorService);
And the rest endpoint:
public class HelloWorldController implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
try (OutputStream responseBody = exchange.getResponseBody()) {
exchange.getResponseHeaders().set("Content-Type", "text/plain");
String payload = "Hello";
exchange.sendResponseHeaders(200, payload.length());
responseBody.write(payload.getBytes(StandardCharsets.UTF_8));
}
}
}
See here for all the details: #82
As you probably already have noticed I am using my own library to easily construct a SSLContext and other properties. Therefor the resulting code is less verbose, but up to you what you prefer.
from mutual-tls-ssl.
While I slept, you've made all the implementation :D
I have the feeling that the answer from stackoverflow and your code snippet can be simplified.
Yep, like I've said, it was just a raw prototype, to get it working. A good candidate to replace most of SSLContext
logic would have been your library, which I see you've already used in your PR.
Regarding the PR, I will walk through it and get familiar with the solution.
Regarding detailed explanation to my questions - thank you!! I'm very grateful for your time and effort.
from mutual-tls-ssl.
Sure, I will try to help you anytime. If you have any other questions don't hesitate to stop by and drop a message 😊
By the way I also added this example to the stackoverflow topic which you have mentioned, it is available here: https://stackoverflow.com/a/65802061/6777695
from mutual-tls-ssl.
Hi again!
You come back with some quite interesting questions 😄 In the past I have tried to configure ssl for Spring Boot programatically, but it is limited to accept only two kind of objects for this use case, see below for the options
- org.springframework.boot.web.server.SslStoreProvider
- org.springframework.boot.web.server.Ssl
None of these two classes could give me the flexibility of configuring ssl with an SSLContext, SSLServerSocketFactory, KeyManager or TrustManager. I have dig into different kind of documentation and also analysed the source code of spring and came into conclusion that it is not possible with Spring Boot & Tomcat. Maybe I have missed something but I am 99% sure that it is not possible.
I've looked for inspiration in your implementation - server-with-spring-boot, but if I'm not mistaken, you're relying on Aspects (namely AdditionalCertificateValidationsAspect) to do X509 validation. Is that correct?
Well that class was for a different use case. I use that to filter out certificates based on the common names while comparing it with a black listed common name list. This happens after a successful ssl handshake but before it reaches the rest controller. It is just an interceptor to do some additional validations, filtering or something else. This is very useful when your server is trusting some CA certificates. With this AdditionalCertificateValidationsAspect
you can still block some clients which have a signed certificate by this CA.
If you don't mind to using a different kind of server engine within Spring Boot I would advise to use Jetty. It is possible to adjust the ssl configuration of Jetty programatically. You can configure it with a custom TrustManager etc. which will enable you to use multiple truststores such as your own and the jdk trust store.
What you need to do is exclude first tomcat from your pom and include Jetty server:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
package nl.altindag.server;
import nl.altindag.ssl.SSLFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import java.util.Collections;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Bean
public SslContextFactory.Server sslContextFactory() {
SSLFactory sslFactory = SSLFactory.builder()
.withIdentityMaterial("identity.jks", "secret".toCharArray())
.withTrustMaterial("truststore.jks", "secret".toCharArray())
.withDefaultTrustMaterial()
.build();
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setSslContext(sslFactory.getSslContext());
sslContextFactory.setIncludeProtocols(sslFactory.getSslParameters().getProtocols());
sslContextFactory.setIncludeCipherSuites(sslFactory.getSslParameters().getCipherSuites());
sslContextFactory.setNeedClientAuth(true);
return sslContextFactory;
}
@Bean
public ConfigurableServletWebServerFactory webServerFactory(SslContextFactory.Server sslContextFactory) {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
factory.setPort(8443);
JettyServerCustomizer jettyServerCustomizer = server -> server.setConnectors(new Connector[]{new ServerConnector(server, sslContextFactory)});
factory.setServerCustomizers(Collections.singletonList(jettyServerCustomizer));
return factory;
}
}
from mutual-tls-ssl.
You come back with some quite interesting questions
This is what happens when I start a topic and really try to dig in :D Say the word if it's bothering you. It's just very rare to find somebody knowledgeable and who's willing to actively share his findings.
None of these two classes could give me the flexibility of configuring ssl with an SSLContext, SSLServerSocketFactory, KeyManager or TrustManager. I have dig into different kind of documentation and also analysed the source code of spring and came into conclusion that it is not possible with Spring Boot & Tomcat. Maybe I have missed something but I am 99% sure that it is not possible.
Ok, that's actually good to know. That means I can stop chasing this, as I was going to sacrifice another few days on this :D
And to the rest. Thanks once again for extensive explanation. Maybe this could be placed somewhere as documentation? Thus in the future, you wouldn't be bother again, with the same question. I think you can even post it into stackoverflow as an answer and I will accept it (link to question).
from mutual-tls-ssl.
Thank you for your nice words!
This page gets indexed by google, so if someone searches on these topics they could this page. You can if you want refer on your stackoverflow question to this page so the community can see a more detailed answer or code examples.
I added an answer to your stackoverflow question a more basic setup without the usage of SSLFactory.
from mutual-tls-ssl.
I have a working solution, without adding stuff which is outlined in my linked Stackoverflow post. Please ignore the ugly code, as this is just a fast prototype:
public class SecureServer {
public static void main(String[] args)
throws IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException, CertificateException, UnrecoverableKeyException {
var server = HttpsServer.create(new InetSocketAddress(8080), 0);
var password = "changeit";
var instance = KeyStore.getInstance(KeyStore.getDefaultType());
var keyStoreFile = Files.newInputStream(Path.of("src/main/resources/certs/nt-ms.jks"));
instance.load(keyStoreFile, password.toCharArray());
var keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(instance, password.toCharArray());
var sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), null, null);
server.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
@Override
public void configure(HttpsParameters params) {
var context = getSSLContext();
var sslparams = context.getDefaultSSLParameters();
params.setSSLParameters(sslparams);
}
});
server.createContext("/", new MyHandler());
server.start();
}
private static class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
var is = t.getRequestBody();
read(is); // .. read the request body
var response = "This is the response";
t.sendResponseHeaders(200, response.length());
var os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
private void read(InputStream is) {
var bufferedReader = new BufferedReader(new InputStreamReader(is));
bufferedReader.lines().forEach(System.out::println);
}
}
}
However, at the end it seem SSLEngine is not closed correctly (?).
javax.net.ssl|ALL|10|HTTP-Dispatcher|2021-01-19 19:36:22.845 EET|SSLEngineImpl.java:786|Closing inbound of SSLEngine
javax.net.ssl|ERROR|10|HTTP-Dispatcher|2021-01-19 19:36:22.850 EET|TransportContext.java:361|Fatal (INTERNAL_ERROR): closing inbound before receiving peer's close_notify (
"throwable" : {
javax.net.ssl.SSLException: closing inbound before receiving peer's close_notify
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:133)
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:356)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:312)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:303)
at java.base/sun.security.ssl.SSLEngineImpl.closeInbound(SSLEngineImpl.java:796)
at jdk.httpserver/sun.net.httpserver.SSLStreams$InputStream.close(SSLStreams.java:581)
at jdk.httpserver/sun.net.httpserver.HttpConnection.close(HttpConnection.java:133)
at jdk.httpserver/sun.net.httpserver.ServerImpl.closeConnection(ServerImpl.java:476)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:669)
at jdk.httpserver/sun.net.httpserver.ServerImpl$DefaultExecutor.execute(ServerImpl.java:159)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Dispatcher.handle(ServerImpl.java:442)
at jdk.httpserver/sun.net.httpserver.ServerImpl$Dispatcher.run(ServerImpl.java:408)
at java.base/java.lang.Thread.run(Thread.java:832)}
)
javax.net.ssl|WARNING|10|HTTP-Dispatcher|2021-01-19 19:36:22.850 EET|SSLEngineOutputRecord.java:173|outbound has closed, ignore outbound application data
from mutual-tls-ssl.
Hi Laurynas
Adding a server example from sun sounds as a nice addition to this project. I have the feeling that the answer from stackoverflow and your code snippet can be simplified. I will add this setup and hopefully it can be useful for you
By the way can you rerun your server with the following VM argument: -Djdk.tls.acknowledgeCloseNotify=true
it might resolve your issue. Looking at this https://bugs.openjdk.java.net/browse/JDK-8208526 bug report it looks like it is resolved but I need to try it out for myself to give you a better direction
from mutual-tls-ssl.
@MrR0807 I added a basic setup for a server with a GET request including setting up ssl.
See here for the details: #82
from mutual-tls-ssl.
Hey, Hakan,
Once again, I'm writing with a question. I didn't want to open a new issue, but just to see whether you've encountered something similar.
Problem
Programmatically configure Spring Boot's TLS configuration (not declaratively).
Context
Currently, I'm playing around with Spring-Boot's TLS and mTLS. Their documentation provides only one, clear way how to configure SSL (via application
):
server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.key-password=another-secret
server.ssl.trust-store=classpath:truststore.jks
...
However, this solution lacks depth. Say, I'd like for my TrustStore
to contain both custom certificate (self-signed) and Java's default (usually placed in lib/security/cacerts
).
One way is to combine both truststore
s using keytool
.
Another way, is building your own SSLContext
relying on CompositeX509ExtendedTrustManager
. Unfortunately, there is no clear path how one could instruct Spring Boot or Spring programmatically to use my custom SSLContext
. There is a section on Configure the Web Server, which says to use either something like TomcatServletWebServerFactory
or ConfigurableServletWebServerFactory
, but they do not really go into depth.
I've looked for inspiration in your implementation - server-with-spring-boot
, but if I'm not mistaken, you're relying on Aspects (namely AdditionalCertificateValidationsAspect
) to do X509 validation. Is that correct?
Anyway, I've tried to achieve this goal via WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
and factory.setSslStoreProvider(new SslStoreProvider() {...}
, but it doesn't work. Also, WebServerFactoryCustomizer<TomcatServletWebServerFactory>
has some settable properties regarding TLS, but they aren't clear.
from mutual-tls-ssl.
I've also created stackoverflow question: https://stackoverflow.com/questions/65890334/configure-spring-boots-with-custom-sslcontext-programmatically-for-mtls.
from mutual-tls-ssl.
My focus has shift towards Tomcat (embedded server which I'm using with Spring Boot). Because in the end, Spring is just a wrapper around it. I'm still looking for a way to configure using SSLContext
, but I don't there is a way.
from mutual-tls-ssl.
Related Issues (19)
- javax.net.ssl.SSLException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty HOT 33
- 找不到客户端启动ssl认证的配置 HOT 4
- class path resource [identity.jks] cannot be resolved to URL because it does not exist HOT 5
- Asking help about the No Subject Alternative Name HOT 4
- Asking help about the check the Certificate Status with OCSP with TLS Connection? HOT 1
- Can't call the server with 2-way TLS based on trusting CA authority - javax.net.ssl.SSLHandshakeException: None of the TrustManagers trust this server certificate chain HOT 7
- Access Server using curl command HOT 9
- A request for examples using standard servers with JAX-RS instead of SpringBoot HOT 1
- Server example with jersey and tomcat HOT 1
- Server example with jboss wildfly HOT 1
- alias does not exist HOT 2
- Missing instructions for running ClientRunnerIT HOT 3
- Build with mvn is not working HOT 2
- Specif CLASSPATH setting instructions would be beneficial HOT 3
- An explicit instructions which tell how I can run ClientRunnerIT would be beneficial. HOT 3
- Enable Client Cert validation only for a single end point at Service end (Spring Boot Java)? HOT 6
- All client tests fail, potential regression from a dependency HOT 7
- Invalid Keystore Format HOT 11
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mutual-tls-ssl.