How to use the certificate for Tomcat

Hey there.

I am a beta participant and have used the client to obtain a certificate for the domain dynamically associated with my NAS.
Since I can’t run the client directly on my NAS I used manual mode to get the cert and imported it on my Synology NAS (chain, cert and privkey). So now I can access my NAS and all things hosted on my NAS like my simple homepage and WordPress blog by HTTPS and use a valid and trusted certificate, which is nice.
However tomcat does its own thing, runs on port 7070 for HTTP and needs additional configuration to accept HTTPS traffic on its default secure port 8443. Following the tomcat documentation I created a java keystore and configured the connector for port 8443 in tomcat’s server.xml file. I added both the chain and cert file to the keystore, but connection attempts to my domain on port 8443 time out. I wonder how this is supposed to work without the privkey, but the tomcat documentation doesn’t mention the need for a key or maybe I’m just interpreting it wrong.

Is there any one out there who attempted to use the output of let’s encrypt client’s manual mode for tomcat?

Any feedback is much appreciated. Thanks in advance.

1 Like

Further information: I believe the tomcat docs don’t mention the private key because they assume that you are using an existing keystore that was used for the CSR and thus contains the private key already (at least I think so…).

So I guess I am required to add the privkey generated from the LE client into the keystore but I am unable to do so because I don’t have the password for the privkey :-/

Just for the record: It can be done. I managed to create a Java Keystore, which can be used by Tomcat.

You do need to add the privkey.pem to the JKS, but I couldn’t do so directly with keytool or Portecle and used a workaround instead.
With the help of openSSL you can create a PKCS12 keystore with both your certificate and the private key like so (no password for privkey.pem required):

openssl pkcs12 -export -in cert.pem -inkey privkey.pem -out cert_and_key.p12 -name tomcat -CAfile chain.pem -caname root

I than converted this PKCS12 to a JKS:

keytool -importkeystore -deststorepass <changeit> -destkeypass <changeit> -destkeystore MyDSKeyStore.jks -srckeystore cert_and_key.p12 -srcstoretype PKCS12 -srcstorepass <thePasswordUsedInTheCommandAbove> -alias tomcat

After that I added the chain.pem (although this might have been avoided with the use of -chain in the openSSL command, I guess):

keytool -import -trustcacerts -alias root -file chain.pem -keystore MyDSKeyStore.jks

The resulting JKS can be used in a Tomcat Connector configuration like so:


So this allows you to use your LE certificate with Tomcat =)
Hope this helps.

2 Likes

I cannot, for the life of me, get this to work. I’ve lost probably 15 hours of my life trying to get LE to work with a java keystore (via dropwizard). I can’t seem to get past the following error:

Exception in thread “main” MultiException[java.security.cert.CertificateException: Unable to validate certificate: unable to find valid certification path to requested target, java.security.cert.CertificateException: Unable to validate certificate: unable to find valid certification path to requested target, java.util.concurrent.RejectedExecutionException: org.eclipse.jetty.util.thread.NonBlockingThread@c65a5ef]

I’ve tried copying your steps directly, as well as every conceivable variation on the steps I can think of, and nothing seems to work.

Are you sure there isn’t anything else you did?

In fact, when I use openssl s_client -connect hostname:port, it shows that it’s failing to get the local issuer certificate:

verify error:num=20:unable to get local issuer certificate

which, when I look at it in the “Certificate chain” section of the output, is expected to be:

i:/C=US/O=Let’s Encrypt/CN=Let’s Encrypt Authority X1

When I look at the certs in keytool (keytool -v -list -keystore keystore.jks), I see that I have two certs in there, a PrivateKeyEntry for my domain, issued by LE, and a trustedCertEntry for LE, issued by DST Root CA X3.

Is there something special I need to do get the two certs to connect to each other? Maybe give it a specific alias or something? Or do I need to fiddle with my trust store to make it accept LE?

The problem is that this stuff is all so intentionally cryptic that I don’t know how to troubleshoot my own issues.

This worked like a charm!

However, I cannot get my iphone to trust it. I read this: Missing Intermediate Certificate Means SmartPhones Don’t Trust Site saying it might have a chain.pem missing. However I followed your steps so the chain must be on it, right? Besides when I try to add either fullchain or chain, it says it’s already there under some alias. Any ideas what I might be missing? Thanks!

This conversion method works and my tomcat is now serving SSL. :grinning:
But a test in SSL Labs shows

Additional Certificates (if supplied)
Certificates provided   1 (1587 bytes)
Chain issues    Incomplete

Is it correct my keystore has 2 keys? I get the “cannot be trusted” message under Android and iPad too.
May I double-check your keystore has only 2 keys too?

keytool -list -keystore MyDSKeyStore.jks 

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 2 entries

root, Dec 6, 2015, trustedCertEntry, 
Certificate fingerprint (SHA1): XX:
tomcat, Dec 6, 2015, PrivateKeyEntry, 
Certificate fingerprint (SHA1): YY:

Thanks!

Wow! So many questions :smiley:
I am happy to see that I am not alone on this one =)

Sorry for taking so long to reply. I will try to be of any help.

@jrf: I don’t have issues on my Android phone:

I have no iPhone to test with.

I don’t seem to have any chain issues like @szw has. Check for your self here: https://www.ssllabs.com/ssltest/analyze.html?d=melo.myds.me

But the SSL Labs output is diffent than in @szw’s case:

Additional Certificates (if supplied)
Certificates provided	2 (2497 bytes)
Chain issues	None 

Please note! This SSL Labs output is from my webserver and not from my Tomcat, since I do not run my Tomcat on 443 and thus I can’t test it with SSL Labs. I have no URL on 443 redirecting to 8443.

My JKS has two entries:

keytool -list -keystore MyDSKeyStore.jks 
Enter keystore password:  

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 2 entries

root, Nov 13, 2015, trustedCertEntry, 
Certificate fingerprint (SHA1): XX
tomcat, Nov 13, 2015, PrivateKeyEntry, 
Certificate fingerprint (SHA1): YY

As in @galactoise case one entry by the alias of root is for Let’s Encrypt and issued by DST Root CA X3 and the other entry by the alias of tomcat is for my domain and issued by Let’s Encrypt.
The AuthorityKeyIdentifier in my PrivateKeyEntry matches the SubjectKeyIdentifier of Let’s Encrypt’s trustedCertEntry, as is to be expected, since LE issued/validated my cert.

I am very sure that I am not forgetting any steps, because I documented the steps at the time of execution, since I was (and I am still) thinking about writing a blog post about this.

  1. You create a PKCS12 that contains both your cert and the private key

  2. You convert that PKCS12 to a JKS

  3. And you finally add the chain to it as trusted CA certificate

I did not come up with the solution all on my own. It is based on what I learned from here: http://stackoverflow.com/a/8224863

(FYI: This site here is also quite convenient and worth a bookmark: http://shib.kuleuven.be/docs/ssl_commands.shtml)

I believe the connector configuration is critical, so please check it twice and make sure you provide the keyAlias with the alias you used for your JKS’s PrivateKeyEntry (in my case this is “tomcat”).

@galactoise: I did run openssl s_client -connect melo.myds.me:8443 like you did but I am not sure if this command is really helping to debug your problem. I also have negative output with that command but am not really sure what to do with it.

melo@melo-desktop:~$ openssl s_client -connect melo.myds.me:8443
CONNECTED(00000003)
depth=0 CN = melo.myds.me
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = melo.myds.me
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/CN=melo.myds.me
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
---
Server certificate
-----BEGIN CERTIFICATE-----
[etc...]
-----END CERTIFICATE-----
subject=/CN=melo.myds.me
issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1825 bytes and written 481 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-SHA256
    Session-ID: 566420061B0676E8B2B925896140041851D6CEC0B806D223D5AB58A978C20573
    Session-ID-ctx: 
    Master-Key: 2A1736E597D2CDD2A0E8A5C60A4281B14550BAA9B08FAFEF69FABBB91BE6757BDB511AAB5FB7DD97CA375A2FFD4679D9
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1449402375
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---
closed

Hope to be of any help with this and wish you good luck =)

Hi all. I have to apologize! I provided you with wrong directions… :pensive:

Thanks to @galactoise for pointing out the use of openssl s_client to test.

I noticed a difference between
openssl s_client -connect melo.myds.me:443
and
openssl s_client -connect melo.myds.me:8443

The webserver did serve everything correctly, so the output was like

CONNECTED(00000003)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X1
verify return:1
depth=0 CN = melo.myds.me
verify return:1
---
Certificate chain
 0 s:/CN=melo.myds.me
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---

However tomcat on port 8443 returned the same issue @galactoise encountered:

CONNECTED(00000003)
depth=0 CN = melo.myds.me
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = melo.myds.me
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/CN=melo.myds.me
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
---

Then it came to me: What if the JKS contained only one entry that represents the whole chain?
So here is what I did:

  1. create a PKCS12 that contains both your full chain and the private key, which for me looked like this:
    openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out fullchain_and_key.p12 -name tomcat

  2. convert that PKCS12 to a JKS, which looks like this:
    keytool -importkeystore -deststorepass changeit -destkeypass changeit -destkeystore MyDSKeyStore.jks -srckeystore fullchain_and_key.p12 -srcstoretype PKCS12 -srcstorepass changeit -alias tomcat

I replaced the old JKS with this one, restarted tomcat and now I get a successful connection with openssl s_client -connect melo.myds.me:8443

@galactoise Sorry for wasting your time by providing wrong directions. Can you give it a try with the fullchain and tell us if this works correctly for you?

4 Likes

OMG, using fullchain.pem fixes the problem!
@melo, thanks! :+1:

SSL Labs now says:

Additional Certificates (if supplied)
Certificates provided 2 (2783 bytes)
Chain issues None

Keystore will now show 1 key, instead of 2.

keytool -list -keystore MyDSKeyStore.jks
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

tomcat, Dec 7, 2015, PrivateKeyEntry,

Android now shows "This certificate is valid"
There is no more complain from iPad either. :beers:

Thanks @melo – it also works for me now. I spent several hours yesterday investigating other routes (converting PEM to DER first, for instance) but this method now works flawlessly. Thanks so much!

@szw, @ksclarke I am very happy to hear that it is working out for you, cheers! :beers:

It is nice to have this place to discuss this topic. I sure learned something :smile:

Wooohoo @melo! It works! Thanks you have been extremely helpful!! Indeed I had chain issues.

I did exactly what you explained last (created PKCS12 containing both the full chain and private key, then converted it to jks). That seems like the way to go!!! Works on iOS and Android, and the ssllabs test is OK. You should definitely write that blog post about it. Couldn’t have done it without the help in this thread. :smile:

It works for me too :slightly_smiling:

But what are the steps to renew the keystore when I get a refreshed certificat? Do I have to create a new keystore or can I use the old one? And do I have to restart Tomcat?

Hi all,
I have created a cript for that here:
http://blog.ivantichy.cz/blogpost/view/74

#!/bin/bash
#author Ivan Tichy
#Please modify these values according to your environment
certdir=/etc/letsencrypt/live/jira.ivantichy.cz/ #just replace the domain name after /live/
keytooldir=/opt/atlassian/jira/jre/bin/ #java keytool located in jre/bin
mydomain=jira.ivantichy.cz #put your domain name here
myemail=xxxxxxx@gmail.com #your email
networkdevice=eth0 #your network device  (run ifconfig to get the name)
keystoredir=/home/jira/.keystore #located in home dir of user that you Tomcat is running under - just replace jira with your user you use for Tomcat, see ps -ef to get user name if you do not know

#the script itself:
cd /var/git/letsencrypt
git pull origin master
iptables -I INPUT -p tcp -m tcp --dport 9999 -j ACCEPT
iptables -t nat -I PREROUTING -i $networkdevice -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9999

./letsencrypt-auto certonly --standalone --test-cert -d $mydomain --standalone-supported-challenges http-01 --http-01-port 9999 --renew-by-default --email $myemail --agree-tos
#./letsencrypt-auto certonly --standalone -d $mydomain --standalone-supported-challenges http-01 --http-01-port 9999 --renew-by-default --email $myemail --agree-tos

iptables -t nat -D PREROUTING -i $networkdevice -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9999
iptables -D INPUT -p tcp -m tcp --dport 9999 -j ACCEPT

$keytooldir/keytool -delete -alias root -storepass changeit -keystore $keystoredir
$keytooldir/keytool -delete -alias tomcat -storepass changeit -keystore $keystoredir

openssl pkcs12 -export -in $certdir/fullchain.pem -inkey $certdir/privkey.pem -out $certdir/cert_and_key.p12 -name tomcat -CAfile $certdir/chain.pem -caname root -password pass:aaa

$keytooldir/keytool -importkeystore -srcstorepass aaa -deststorepass changeit -destkeypass changeit -srckeystore $certdir/cert_and_key.p12 -srcstoretype PKCS12 -alias tomcat -keystore $keystoredir
$keytooldir/keytool -import -trustcacerts -alias root -deststorepass changeit -file $certdir/chain.pem -noprompt -keystore $keystoredir


# restart your Tomcat server – mine is running JIRA
service jira stop
service jira start
1 Like

Hey @dasralph, I renewed my certificate last week or so. I just used the same steps again, which overwrite the original JKS with the new one. However I think Tomcat “caches” the information from the JKS, so I did restart Tomcat, at which point it used the new JKS with the renewed certificates.

I finally got around writing that blog post to summarize how to set up Tomcat 7 for HTTPS with the pem files from Let’s Encrypt client: https://melo.myds.me/wordpress/lets-encrypt-for-tomcat-7-on-ds/

Hope it helps =)

1 Like

Hey @galactoise - did you ever get this working?
I struggled a bit with tomcat as well. My experience is here:

For debugging tomcat issues, the trick is:

  • turn on ssl debugging in tomcat. For ubuntu, in /etc/default/tomcat:

JAVA_OPTS="-Djava.awt.headless=true -Xmx128m -XX:+UseConcMarkSweepGC -Djavax.net.debug=ssl"

(the last arg is the one I added. … the logs are going to be in /var/log/catalina.out (not catalina.[date].log))

You sill get a lot of SSL logs. Looking for log entries like:

(private pem key file looks like ------- BEGIN PRIVATE KEY -------- )
(public pem key file looks like -------- BEGIN CERTIFICATE -------- )

with entries after them for you private key and cert.

Hi everybody!
Thanks a lot for all yours posts.
I’m new here and i’m tryin’ to figure out how to create keystore from existing certificate chain.
I already have private key and certificate for root, intermediate and server.
-ca.key.pem
-ca.cert.pem
-intermediate.key.pem
-intermediate.cert.pem
-www.mydomain.cert.pem
-www.mydomain.key.pem
I have also a file named ca-chain.cert.pem which contain ca and intermediate certificates and maintain their chain.
So i am wondering what your “full chain” contain and how i can create my keystore with the certificate chain, and then activate rtmps and https on my red5 server
thanks

You’ve got the private key for the root and intermediate certificates? Nice! :stuck_out_tongue:

fullchain.pem just contains cert.pem and chain.pem, your servers and intermediate certificate respectively.

1 Like

ok
So if im good, to get fullchain.pem all i have to do is :
cat ca.cert.pem intermediate.cert.pem www.mydomain.cert.pem > fullchain.cert
Right?