7

We have a Spring Boot application for REST web services which is still under development. And we are using self signed certificate for now.

Now, it will be deployed into a system along with 1 more already developed application. This pre-existing application uses self signed certificate by default but gives client an option to upload CA certificates if they want. Now, we want to use the same certificate for this new application.

Basically, we want the client to use 1 certificate for 2 application running in 1 system.

Now, this existing application has certificate files like .pem and .cer.
How can I use this certificate in my Spring Boot application which uses certificate in the format of jks?

And off course, in case of any update, the certificate should be available to both of the applications.

AlexElin
  • 1,044
  • 14
  • 23
Onki
  • 1,879
  • 6
  • 38
  • 58

4 Answers4

10

As of Spring Boot 2.7 it's possible to use PEM-encoded certificate and private key files.
See the below example (private key in PKCS8 format).

server:
  port: 8443
  ssl:
    certificate: "classpath:my-cert.crt"
    certificate-private-key: "classpath:my-cert.key"
    trust-certificate: "classpath:ca-cert.crt"

Spring docs 2.7.2

AlexElin
  • 1,044
  • 14
  • 23
  • We have PEM-encoded certificate and private key files which the private key file is encrypted; I mean the header of file is `---BEGIN ENCRYPTED PRIVATE KEY---`. Your solution worked after decrypting private key file only. Actually spring says `java.lang.IllegalStateException: Unrecognized private key format`. The section `key-store-password` does not work and I had to decrypt private key manually. – Askar Nov 23 '22 at 12:29
  • Spring throws that exception becasue it actually doesn't support your format of private key. See supported formats in the Spring's [`PrivateKeyParser`](https://github.com/spring-projects/spring-boot/blob/2.7.x/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/PrivateKeyParser.java) – AlexElin Nov 24 '22 at 12:23
  • 1
    what's "trust-certificate" here, and what should I add in that field, since I only have a certificate and private key files. – Prasannjeet Singh Jan 22 '23 at 11:05
  • @Askar see my answer for an example with the usage of encrypted private keys https://stackoverflow.com/a/76549847/6777695 – Hakan54 Jun 25 '23 at 10:07
  • @Hakan54 thanks for your help, This answer has been changed after my previous comment. I have solved the problem. – Askar Jun 28 '23 at 08:36
5

PEM is a well-known file format when it comes to certificates. Except when it comes to Java. As Java does only use JKS (its Java-only, binary Keystore) or PKCS12 for keys and certificates. So we have to convert PEM encoded certificates to JKS or PKCS12 so that Java can consume that. But that may be ugly in a lot of situations.

you can use below dependency in your spring-boot application.

<dependency>
  <groupId>de.dentrassi.crypto</groupId>
  <artifactId>pem-keystore</artifactId>
  <version>2.0.0</version>
</dependency>

then add

KeyStore keyStore = KeyStore.getInstance("PEM");

for more info

https://github.com/ctron/pem-keystore

application.properties

 server.ssl.enabled=true
 server.ssl.key-store=/path/to/keystore.properties
 server.ssl.key-store-type=PEMCFG
 server.ssl.key-store-password=dummy
 server.ssl.key-alias=keycert

And then you create the file keystore.properties:

alias=keycert
source.cert=/etc/…/fullchain.pem
source.key=/etc/…/privkey.pem
  • we want to avoid any 3rd party jars. Looking at this , do you have any other approach to suggest – Onki Oct 11 '19 at 03:54
  • i got idea on the problem statement. I was not aware that java does not use .PEM format. Now I have to figure out a cleaner way to use .pem in my spring boot application. I want to use api which are provided by java or spring boot. Or convert this at platform level to JKS from pem and then use it in java. Please do suggest if you can think of better apporach or design using these limited options. – Onki Oct 11 '19 at 04:35
  • I suggest you extract that jar content then you can modify the source code as per your requirement. I hope it will help you to avoid using third party jars – Bala S Singh Oct 11 '19 at 06:48
  • @BalaSSingh could you please provide sample code on how to create SSLContext with the KeyStore above ? – Kriss Feb 13 '20 at 21:31
0

What I often do is replace Tomcat with Jetty within the Spring, which does allow custom ssl configuration. With that kind of setup any kind of customization is possible such as handling pem files, but also especially encrypted pem files. Below is the setup which I use:

Below the pem files will be loaded and a KeyManager TrustManager will be created. This will be used to create a SSLFactory, which in return will be used to convert to a Jetty compatible ssl configuration.

import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.util.JettySslUtils;
import nl.altindag.ssl.util.PemUtils;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;

@Configuration
public class SSLConfig {

    @Bean
    public SSLFactory sslFactory() {
        X509ExtendedKeyManager keyManager = PemUtils.loadIdentityMaterial("chain.pem", "private-key.pem", "my-password".toCharArray());
        X509ExtendedTrustManager trustManager = PemUtils.loadTrustMaterial("some-trusted-certificate.pem");

        return SSLFactory.builder()
                .withIdentityMaterial(keyManager)
                .withTrustMaterial(trustManager)
                .build();
    }

    @Bean
    public SslContextFactory.Server sslContextFactory(SSLFactory sslFactory) {
        return JettySslUtils.forServer(sslFactory);
    }

}

The Jetty SSL configuration needs to be passed on to the server configuration:

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.springframework.beans.factory.annotation.Value;
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 org.springframework.context.annotation.Configuration;

import java.util.Collections;

@Configuration
public class ServerConfig {

    @Bean
    public ConfigurableServletWebServerFactory webServerFactory(SslContextFactory.Server sslContextFactory, @Value("${server.port}") int serverPort) {
        JettyServletWebServerFactory factory = new JettyServletWebServerFactory();

        JettyServerCustomizer jettyServerCustomizer = server -> {
            ServerConnector serverConnector = new ServerConnector(server, sslContextFactory);
            serverConnector.setPort(serverPort);
            server.setConnectors(new Connector[]{serverConnector});
        };
        factory.setServerCustomizers(Collections.singletonList(jettyServerCustomizer));

        return factory;
    }

}

After these two java configurations you also need to tell Spring not to use Tomcat, but it should use Jetty. This can be done with the following pom configuration:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot</artifactId>
    <version>2.7.5</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.7.5</version>
    <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>
    <version>2.7.5</version>
</dependency>

I used my own library to read the pem files. See below for the dependencies and here for more regarding other usages: SSLContext-Kickstart

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-jetty</artifactId>
    <version>8.1.2</version>
</dependency>
<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-pem</artifactId>
    <version>8.1.2</version>
</dependency>
Hakan54
  • 3,121
  • 1
  • 23
  • 37
-3

for testing secured API you can use Fiddler a tool for bypassing or faking the SSL.

(OR)

you can configure application properties like below.

server.port: 8443
security.require-ssl=true
server.ssl.key-store:/etc/letsencrypt/live/seeld.eu/keystore.p12
server.ssl.key-store-password: <your-password>
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: tomcat