Git Product home page Git Product logo

Comments (12)

Hakky54 avatar Hakky54 commented on July 25, 2024 1

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.

MrR0807 avatar MrR0807 commented on July 25, 2024 1

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.

Hakky54 avatar Hakky54 commented on July 25, 2024 1

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.

Hakky54 avatar Hakky54 commented on July 25, 2024 1

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.

MrR0807 avatar MrR0807 commented on July 25, 2024 1

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.

Hakky54 avatar Hakky54 commented on July 25, 2024 1

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.

MrR0807 avatar MrR0807 commented on July 25, 2024

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.

Hakky54 avatar Hakky54 commented on July 25, 2024

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.

Hakky54 avatar Hakky54 commented on July 25, 2024

@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.

MrR0807 avatar MrR0807 commented on July 25, 2024

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 truststores 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.

MrR0807 avatar MrR0807 commented on July 25, 2024

I've also created stackoverflow question: https://stackoverflow.com/questions/65890334/configure-spring-boots-with-custom-sslcontext-programmatically-for-mtls.

from mutual-tls-ssl.

MrR0807 avatar MrR0807 commented on July 25, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.