How to use the certificate for Tomcat

Thanks for the info @melo. Then could it be because the DST Root X3 certificate is not included in the default java cacerts?

Others having the same problem discussed here: https://github.com/ebekker/ACMESharp/issues/119

@sintroo I guess it depends on the used java version. Java comes with keytool. You can use it to list the entries of a keystore. To list the trusted CA certs you could try running this on your server:
keytool -list -v -keystore $JAVA_HOME/jre/lib/security/cacerts

But I am not sure what your problem is exactly.
The DST Root X3 cert missing from the java trusted CA store would only be relevant when accessing your server with a java client and trying to validate the provided certificate.
There is nothing you need to to on your server for the DST Root X3 cert.

Hello, melo.

Thank you for your solution.
Also I’d like to tell that it can be used pkcs12 keystore without jks converting.

Server.xml should look like this.

`< Connector
port="443"
protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="200"
SSLEnabled="true"
scheme="https"
secure="true"
clientAuth="false"
sslProtocol=“TLS"
keystoreFile=”[path to p12-file]“
keystoreType=“PKCS12"
keystorePass=”[selected pkcs12 keystore password]” />

`

Hi @vaysman, thank you for your feedback!

This confirms my suspicion that the convert to JKS step is unnecessary.
One step less means slightly reduced chance to make mistakes =)
Thank you for sharing the connector configuration.

For me this means that in future I can just use the PKCS12 file and only need to change the keystoreFile value accordingly and add the keystoreType attribute with value PKCS12 to my connector.

1 Like

Thanks @melo, I saw your updated command further down in the other thread. This worked great for me and I am up and running in SpringBoot. If anyone is interested in what I did to get working with the embedded tomcat in SpringBoot, just reply and I’ll post.

1 Like

Thanks @melo, I struggled for so many hours and thanks to your guide I now have a working tomcat server on a raspberry pi with free ssl certificates from let’s encrypt and a free subdomain from selfhost.eu :slight_smile:

1 Like

I have a new preferred solution for HTTPS to Tomcat with Let’s Encrypt certificate. I do not configure Tomcat for HTTPS in the first place!
In case your setup consists of both a web server and Tomcat, consider setting up a reverse proxy on your web server and let it handle HTTPS for Tomcat. This way you can benefit from Let’s Encrypts automatic certifcate renewal capabilities that update the certificates your web server uses and you are still able to securely access Tomcat through HTTPS without manual keystore creation or anything like that.
I use a Synology DiskStation, which added LE support and a reverse proxy feature and I now have a fully automatic, one time setup solution I really like =)

In case anyone is interested in this specific setup, I described it in a blog post:

Hi @vlott, I am running a SpringBoot application with embedded Tomcat and am trying to get it to work with Let’s Encrypt, and searching through the forum came across your comment here. Did you post the steps you used? It would be super super helpful to us. I’ve installed certbot, and generated fullchain.pem & the PKCS12 file, as in some comments above, but am getting errors on the conversion to JKS (keytool gives me a BadPaddingException).

Did you take the route of just using the PKCS12 file with Spring-Boot/Tomcat and if so how did it go? Or did you use the reverse proxy route?

Here are the steps I used to generate certificates and get them working for all browsers in Spring Boot:

  • Make sure firewall will allow the letsencrypt client to receive a callback on port 80 of the server at the domain(s) being configured in the certificate.
  • Run the letsencrypt client:
    sudo /home/ec2-user/letsencrypt/letsencrypt-auto certonly --standalone --standalone-supported-challenges http-01
  • When prompted enter the domains that the letsencrypt server will call back to verify as per above
  • You should have a successful run and you should see the certificates in the /etc/letsencrypt/live directory
  • Create PKCS12 keystore (to be imported to jks later):
    sudo openssl pkcs12 -export -in /etc/letsencrypt/archive/yourDomain.com/fullchainx.pem -inkey /etc/letsencrypt/archive/yourDomain.com/privkeyx.pem -out yourDomain-cert_and_key.p12 -name tomcat (where fullchainx is fullchain1.pem, fullchain2.pem, etc., from the new certificate. This will be incremented every time new certs are generated. IMPORTANT! Use the fullchainx.pem instead of the certx or chainx or it won't work in Firefox)
  • Convert PKCS12 Keystore to Java Keystore:
    /usr/java/jre1.7.0_65/bin/keytool -importkeystore -deststorepass yourKeyPassword -destkeypass yourPassword -destkeystore yourDomain-keystore.jks -srckeystore yourDomain-cert_and_key.p12 -srcstoretype PKCS12 -srcstorepass yourPassword -alias tomcat
  • Check Java Keystore:
    keytool -list -keystore yourDomain-keystore.jks
  • Copy keystore file to springBootJarLocation/ssl/yourDomain-keystore.jks on prod server

Spring Boot configuration

  • Add entries to prod environment and configure SpringBoot to read keystore off disk (application-prod.properties) (and autowire into Configuration-annotated class)

custom.server.port=8443
custom.server.ssl.key-store=file:/opt//ssl/yourDomain-keystore.jks
custom.server.ssl.key-store-password=yourPassword
custom.server.ssl.key-password=yourPassword

  • Add entries as needed to local environment for testing also

  • Add a bean for a custom EmbeddedServletContainerFactory that configures all requests to be encrypted and redirected to SSL. Also, this bean will add a second connector for port 443/SSL:

    @Bean

  public EmbeddedServletContainerFactory servletContainer() {
      TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {
          \@Override
          protected void postProcessContext(Context context) {
              SecurityConstraint securityConstraint = new SecurityConstraint();
              securityConstraint.setUserConstraint("CONFIDENTIAL");
              SecurityCollection collection = new SecurityCollection();
              collection.addPattern("/*");
              securityConstraint.addCollection(collection);
              context.addConstraint(securityConstraint);
          }

      };
      tomcat.addAdditionalTomcatConnectors(createSslConnector());
      return tomcat;
  }

  private Connector createSslConnector() {
      Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
      Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
      try {
          File keystore = new UrlResource(serverSslKeystore).getFile();
          File truststore = new UrlResource(serverSslKeystore).getFile();
          connector.setScheme("https");
          connector.setSecure(true);
          connector.setPort(serverPort);
          protocol.setSSLEnabled(true);
          protocol.setKeystoreFile(keystore.getAbsolutePath());
          protocol.setKeystorePass(serverSslKeystorePassword);
          protocol.setKeyPass(serverSslKeyPassword);
          protocol.setTruststoreFile(truststore.getAbsolutePath());
          protocol.setTruststorePass(serverSslKeystorePassword);
          protocol.setKeyAlias("tomcat");
          return connector;
      } catch (IOException ex) {
          throw new IllegalStateException("can't access keystore: [" + "keystore"
                  + "] or truststore: [" + "keystore" + "]", ex);
      }
  }

@vlott: Thank you! Just followed those and have the cert installed and all working very nicely. Thanks a lot for the quick reply & clear instructions.

Tomcat 8.5.3 now has native support for openssl

Assuming you are using OpenSSL, & Tomcat 8.5.3 you can use the LetsEncrypt certificat *.pem files directly like this:

example connector in server.xml:

2 Likes

I get an exception with tomcat 9.0.0.M8:

29-Jul-2016 09:58:52.366 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
29-Jul-2016 09:58:52.384 INFO [main] org.apache.tomcat.util.net.NioSelectorPool.getSharedSelector Using a shared selector for servlet write/read
29-Jul-2016 09:58:52.387 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["https-jsse-nio-8443"]
29-Jul-2016 09:58:52.742 SEVERE [main] org.apache.coyote.AbstractProtocol.init Failed to initialize end point associated with ProtocolHandler ["https-jsse-nio-8443"]
 java.security.KeyStoreException: Cannot store non-PrivateKeys
    at sun.security.provider.JavaKeyStore.engineSetKeyEntry(JavaKeyStore.java:258)
    at sun.security.provider.JavaKeyStore$JKS.engineSetKeyEntry(JavaKeyStore.java:56)
    at sun.security.provider.KeyStoreDelegator.engineSetKeyEntry(KeyStoreDelegator.java:117)
    at sun.security.provider.JavaKeyStore$DualFormatJKS.engineSetKeyEntry(JavaKeyStore.java:70)
    at java.security.KeyStore.setKeyEntry(KeyStore.java:1140)
    at org.apache.tomcat.util.net.jsse.JSSEUtil.getKeyManagers(JSSEUtil.java:302)
    at org.apache.tomcat.util.net.AbstractJsseEndpoint.initialiseSsl(AbstractJsseEndpoint.java:90)
    at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:245)
    at org.apache.tomcat.util.net.AbstractEndpoint.init(AbstractEndpoint.java:839)
    at org.apache.coyote.AbstractProtocol.init(AbstractProtocol.java:558)
    at org.apache.coyote.http11.AbstractHttp11Protocol.init(AbstractHttp11Protocol.java:65)
    at org.apache.catalina.connector.Connector.initInternal(Connector.java:1010)
    at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:107)
    at org.apache.catalina.core.StandardService.initInternal(StandardService.java:549)
    at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:107)
    at org.apache.catalina.core.StandardServer.initInternal(StandardServer.java:873)
    at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:107)
    at org.apache.catalina.startup.Catalina.load(Catalina.java:606)
    at org.apache.catalina.startup.Catalina.load(Catalina.java:629)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.startup.Bootstrap.load(Bootstrap.java:311)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:494)

29-Jul-2016 09:58:52.744 SEVERE [main] org.apache.catalina.core.StandardService.initInternal Failed to initialize connector [Connector[HTTP/1.1-8443]]
 org.apache.catalina.LifecycleException: Failed to initialize component [Connector[HTTP/1.1-8443]]
    at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:112)
    at org.apache.catalina.core.StandardService.initInternal(StandardService.java:549)
    at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:107)
    at org.apache.catalina.core.StandardServer.initInternal(StandardServer.java:873)
    at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:107)
    at org.apache.catalina.startup.Catalina.load(Catalina.java:606)
    at org.apache.catalina.startup.Catalina.load(Catalina.java:629)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.startup.Bootstrap.load(Bootstrap.java:311)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:494)
Caused by: org.apache.catalina.LifecycleException: Protocol handler initialization failed
    at org.apache.catalina.connector.Connector.initInternal(Connector.java:1013)
    at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:107)
    ... 12 more
Caused by: java.security.KeyStoreException: Cannot store non-PrivateKeys
    at sun.security.provider.JavaKeyStore.engineSetKeyEntry(JavaKeyStore.java:258)
    at sun.security.provider.JavaKeyStore$JKS.engineSetKeyEntry(JavaKeyStore.java:56)
    at sun.security.provider.KeyStoreDelegator.engineSetKeyEntry(KeyStoreDelegator.java:117)
    at sun.security.provider.JavaKeyStore$DualFormatJKS.engineSetKeyEntry(JavaKeyStore.java:70)
    at java.security.KeyStore.setKeyEntry(KeyStore.java:1140)
    at org.apache.tomcat.util.net.jsse.JSSEUtil.getKeyManagers(JSSEUtil.java:302)
    at org.apache.tomcat.util.net.AbstractJsseEndpoint.initialiseSsl(AbstractJsseEndpoint.java:90)
    at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:245)
    at org.apache.tomcat.util.net.AbstractEndpoint.init(AbstractEndpoint.java:839)
    at org.apache.coyote.AbstractProtocol.init(AbstractProtocol.java:558)
    at org.apache.coyote.http11.AbstractHttp11Protocol.init(AbstractHttp11Protocol.java:65)
    at org.apache.catalina.connector.Connector.initInternal(Connector.java:1010)
    ... 13 more

Relevant part from server.xml:

  <Service name="Catalina">

    <!-- Connector port="8080" protocol="HTTP/1.1"... -->
    <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
               connectionTimeout="20000" redirectPort="8443" />
    
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               defaultSSLHostConfigName="abcde.xyz-informatik.de">
        <SSLHostConfig hostName="abcde.xyz-informatik.de">
            <!-- Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
                         type="RSA" /-->
            <Certificate certificateFile="conf/ssl/abcde.xyz-informatik.de/domain.crt"
                         certificateChainFile="conf/ssl/abcde.xyz-informatik.de/chain.crt"
                         certificateKeyFile="conf/ssl/abcde.xyz-informatik.de/domain.key"
                         certificateKeyPassword=""
                         certificateKeyAlias=""
                         type="RSA" />
        </SSLHostConfig>
    </Connector>

… Maybe reproducing this can be done with uninstalling openssl… Just a guess…
I just tried with tomcat 9.0.0.M9 and it gives completely different exceptions, but does not work neither.
I want to rely purely on java, to not be affected by openssl security holes. So I created a virtual server with no openssl installed in it.

@Coleman-rik - Thanks for the post!

I just wanted to give everyone a heads up that the method @Coleman-rik documented is broken in 8.5.4, so if you are testing this out either use 8.5.3 or wait until the 8.5.5 is released with this fixed. Hopefully this will save some frustration as I just spent a few hours with 8.5.4!

I’m having the same problem as the guy above. I’m using Apache Tomcat/8.5.3 with Java 1.8.0_92 (Oracle) 64bit.

The log files show:

java.security.KeyStoreException: Cannot store non-PrivateKeys

OK, in case someone else has the same problem, you have to activate the Tomcat Native Connector 1.2 by adding a dll to your lib directory. Works fine afterwards.

hi all,

thx u for this forum , need to help regarding setup ssl on jira using exisiting .pem

i have jira im going to put the ssl on , after searching some forum im still unable to resolved it , i saw melo has experience on it , melo could pls help assist me the step if using own certificate , because already had the certificate .
( private.pem , server.crt . testing.crt ) thats all its working on my web server apache and nginx ,

thx,
Lukman

Hi @melo, thanks for all your help. But I cannot acces to https://melo.myds.me/wordpress/reverse-proxy-tomcat-for-https anymore. Is it possible to bring it back online please?

Thank

I have managed to configure Tomcat and Letsencrypt using Tomcat Native APR connector on Ubuntu. Gets A+ rating on SSL test. I’ve written a tutorial how to do it here: https://mladenadamovic.wordpress.com/2016/09/06/configure-tomcat-with-ssl-on-ubuntu-minimal/

Hope it helps!

I’m giving a presentation at this year’s ApacheCon in Miami where I’ll be showing how to use Tomcat with Let’s Encrypt. After the presentation, I’ll be making my slides available on http://tomcat.apache.org/presentations.html for anyone who wants them.

I have made a PoC Video on this topic where it provide all the necessary step to csr generation to configuring tomcat to use lets encrpt provided certificate

1 Like