Configuring Tomcat and Clients for TLS

Table of contents

 

Quick index

Objective
Requirement
Disclaimers
Generating a self-signed certificate
Verifying Tomcat's new keystore
Configuring Tomcat for TLS
Discovering our certificate in Tomcat
Importing the certificate into a keystore
Look inside the client keystore
Deployment to the client
Appendix: list of console commands
 
 
 
keytool-genkeypair     -ext san=dns:tirion
keytool-list                   SAN DNS:tirion
conf/server.xml
openssl-s_client  
keytool-importcert  
keytool-list                   SAN DNS:tirion

Objectives

Overall we want to instrument Tomcat to operate over TLS (HTTPS) and create a certificate that we can pass out to clients of Tomcat's services. This will enable clients to recognize that the target of their requests is in fact the service they want (and not another). It does nothing to authenicate clients as worthy of interacting with services running in Tomcat. (That is possible to do, but does not interest us here.) In this exposé, we...

  1. generate a self-signed certificate for Tomcat's use in identifying itself to (HTTPS) clients,
  2. embed that certificate inside a Java keystore,
  3. configure Tomcat to recognize and consume that keystore; bounce Tomcat,
  4. connect to Tomcat and get a copy of the certificate,
  5. import that certificate in a trust store for clients like Apache NiFi to consume,
  6. configure Apache NiFi's InvokeHTTP processor as an example of a client.

Requirements

Disclaimers

I have chosen to work out of two non-canonical subdirectories.

I do all the work under Tomcat, /opt/tomcat/certificates, except at the very end when I copy client-truststore.jks to NiFi.

This is arbitrary and there is no reason you are obliged to follow suit. You could instead make use of those frameworks' conf subdirectories.

Everywhere in here we use Java JKS' default password, "changeit". Anything else is left as an exercise for the student. (Always wanted to say that.)

Service mdht-restlet is an application based on the Model-driven Health Tools which parse or produce HL7v3 Clinical Document Architecture files. Just think of it as any Tomcat-deployed microservice. Some microservice targetable from Apache NiFi's InvokeHTTP is useful, but really only ultimately to verify the TLS functionality in this exposé.

I have created an entry in /etc/hosts (127.0.0.1 tirion) for use in simulating DNS. Using localhost might work, but it's considered bad practice to create a SAN in a certificate for localhost for the obvious security danger it presents.

Now, let's get started...

Generating a self-signed certificate for Tomcat

Not only will we generate a self-signed certificate to be used in a private arrangement between Tomcat and HTTPS clients, we're going to add "tirion" as if a DNS entry to a short list of subject-alternative names (a list of exactly one SAN).

$ keytool -genkeypair -keyalg RSA -keysize 2048 -validity 365 -dname "CN=tomcat" -ext san=dns:tirion \
          -alias tomcat -keystore tomcat.jks -storepass changeit -keypass changeit
$ ll
total 12
drwxr-xr-x  2 russ tomcat 4096 Aug 11 18:20 .
drwxr-xr-x 11 root tomcat 4096 Aug  2 15:44 ..
-rw-rw-r--  1 russ russ   2553 Aug 11 18:46 tomcat.jks

Verifying Tomcat's new keystore

This is important to prove that our SAN was injected. Inded it was, as highlighted.

$ keytool -list -v -keystore tomcat.jks -storepass changeit
Keystore type: PKCS12
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: tomcat
Creation date: Aug 11, 2022
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=tomcat
Issuer: CN=tomcat
Serial number: 3d907564
Valid from: Thu Aug 11 18:46:24 MDT 2022 until: Fri Aug 11 18:46:24 MDT 2023
Certificate fingerprints:
	 SHA1: 9D:84:10:5A:CF:3D:A9:9D:62:33:92:D1:46:AD:8C:5E:96:CE:F6:28
	 SHA256: 57:2D:8C:AA:1D:3C:8D:19:C3:93:6B:7E:D9:DE:75:63:CA:F3:33:2B:8A:12:8E:D4:52:CE:A5:25:23:32:3E:8A
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3

Extensions:

#1: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
  DNSName: tirion
]

#2: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 19 7B 9E 64 67 C2 E9 33   C4 8B 44 71 7A 1D B2 8E  ...dg..3..Dqz...
0010: 22 A7 6C E4                                        ".l.
]
]

*******************************************
*******************************************

Configuring Tomcat for TLS

This configuration is performed in ${CATALINA_BASE}/conf/server.xml. Look for "Connector" to find the default connection for port 8080 and add this secure definition after it.

What's very different from the <Connector definition for port 8080, Tomcat's default, is highlighted. "certificate/tomcat.jks" is a path relative to ${CATALINA_BASE}.

<Connector port="8443" protocol="HTTP/1.1"
  connectionTimeout="20000"
  scheme="https"
  secure="true"
  SSLEnabled="true">
  <SSLHostConfig>
    <Certificate certificateKeyAlias="tomcat"
      certificateKeystoreFile="certificates/tomcat.jks"
      certificateKeystorePassword="changeit" />
  </SSLHostConfig>
</Connector>

Because our certificate is in place and a reference to it is configured in server.xml, we can bounce Tomcat:

# systemctl restart tomcat

Verifying Tomcat is now capable of TLS (HTTPS)

...and then, in a browser, hit Tomcat using https://localhost:8443/. We'll see the browser complain about an unsecure certificate—that's because ours is self-signed and therefore unknown—and we'll be able to dig through that telling the browser it doesn't matter.

At that point, if we have the Tomcat manager activated, that's what will show up.

Another way to test this is to use curl. We have to use the --insecure option to reassure curl that we know about our unsecure certificate just as we told the browser to ignore it.

$ curl --insecure --request GET https://localhost:8443/

Discovering our certificate in Tomcat

This command connects to the URL (Tomcat in this case) and requests the server's certificates. We put a certificate there; that's what we'll get back.

$ openssl s_client -connect tirion:8443 -showcerts > client.crt
Can't use SSL_get_servername
depth=0 CN = tomcat
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = tomcat
verify return:1
$ ll client.crt
-rw-rw-r-- 1 russ russ 2501 Aug 11 18:49 client.crt
$ cat client.crt    ┌───────────────────────────────────────────────────────┐
CONNECTED(00000003) │ (To be honest: if there's anything that perplexes me  │
---                 │  at all, it's that I don't see the SAN in here while  │
Certificate chain   │  it shows up as if by magic in the next step.)        │
 0 s:CN = tomcat    └───────────────────────────────────────────────────────┘
   i:CN = tomcat
-----BEGIN CERTIFICATE-----
MIIC1DCCAbygAwIBAgIEPZB1ZDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZ0
b21jYXQwHhcNMjIwODEyMDA0NjI0WhcNMjMwODEyMDA0NjI0WjARMQ8wDQYDVQQD
EwZ0b21jYXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCHvoADJD2Y
oQYqCVa7CIVmFFjCbJd/U1s65QSEoTbQ7zc0VaIJ1PrjzTMywNWRh2wHenC1PYV4
JLjXeidOlXH7+Ibpko4KU9TA7RhsjAJpOlZZIXzao5jzyzOPfMnlBlsiM7o0jjB9
d8Lj2KEAfRzgzX+6P7jE9fbw62A3Z2mLQ8NDixv1SoumGySQwJLWJxBoYnRMfLdj
sFtuXJVXHiSoslbt6vN3KVYgYLAKX/viDdYItypXGJObONsd+xiajQEMhT1U9qC3
A0Cx9VUlstANn0Iemr7VdgLYegnqpgDENDvHZUqtUiQdItTWCgKFRYGya/+ci9XB
4UqmtkltFEjBAgMBAAGjNDAyMB0GA1UdDgQWBBQZe55kZ8LpM8SLRHF6HbKOIqds
5DARBgNVHREECjAIggZ0aXJpb24wDQYJKoZIhvcNAQELBQADggEBAHmGYGOK7Elx
UADCVWpjAOC1U07VCicAUqa4HOyfSp2ZIm3uYtsskXTfv0nRsjuYYDGngGi9lZYn
dBk10WaQCuPNBklc4+GKlL087Cg9rPmu+4Vyr4F17SbbZwv3/EeOqShsrhQSQ/38
OrUG5cuDrzOYhoS7HYALDqAkuCiiF2/aa63vGsu9VDCzRi0SpHfmD//ynXSM2bM/
q8YOyxf6R1UYT/5x0lxEGuaiRPXR6iLBqNKDfJKzl38qLNs9/BoN4jnySKgTJB9I
bww9Pc5dnXOecsm6r192Y3VBKHSn7dF+5/Hqf9kPYOj6g10/F/wur8AvVah62X5o
/ZQSgi2Z9HI=
-----END CERTIFICATE-----
---
Server certificate
subject=CN = tomcat
issuer=CN = tomcat
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 1260 bytes and written 363 bytes
Verification error: self signed certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 18 (self signed certificate)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: 343BBD170C7EE800DC57EE54995C1CB49C1359CF450EA723C9E0FA2B46683BB0
    Session-ID-ctx:
    Resumption PSK: 31C6D718594FE331BEB8951DB45A6BC310A24C4B7229066216BF84E5261F12C8D30156DD1D4259D9357D8FA6B4A1EF94
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 86400 (seconds)
    TLS session ticket:
    0000 - 3f 4b d5 d8 99 4f 16 21-96 6a 7a 07 08 28 74 96   ?K...O.!.jz..(t.
    0010 - 3e 34 2a 16 ad c6 6a bd-eb dc 50 3c 27 18 e8 84   >4*...j...P>'...
    Start Time: 1660265338
    Timeout   : 7200 (sec)
    Verify return code: 18 (self signed certificate)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
closed

Importing the certificate into a keystore

...for use by the client. This keystore will be used to configure clients that exchange requests with our Tomcat:

$ keytool -importcert -file client.crt -alias tomcat -keystore client-truststore.jks -keypass changeit -storepass changeit -noprompt
Certificate was added to keystore
$ ll
total 20
drwxr-xr-x  2 russ tomcat 4096 Aug 11 19:31 .
drwxr-xr-x 11 root tomcat 4096 Aug  2 15:44 ..
-rw-rw-r--  1 russ russ   2501 Aug 11 18:49 client.crt
-rw-rw-r--  1 russ russ   1095 Aug 11 19:31 client-truststore.jks
-rw-rw-r--  1 russ russ   2553 Aug 11 18:46 tomcat.jks

Look inside the client keystore

There are a number of reasons to do this. Mostly, we want to observe that there is the subject-alternative name, a DNS name, that we added. If not, client use will error out. What else is interesting might be the SHA256: it must match what we see in Tomcat when we get the certificate and look at it. Again, if it doesn't match, the client will error out.

$ keytool -list -v -keystore client-truststore.jks -storepass changeit
Keystore type: PKCS12
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: tomcat
Creation date: Aug 11, 2022
Entry type: trustedCertEntry

Owner: CN=tomcat
Issuer: CN=tomcat
Serial number: 3d907564
Valid from: Thu Aug 11 18:46:24 MDT 2022 until: Fri Aug 11 18:46:24 MDT 2023
Certificate fingerprints:
	 SHA1: 9D:84:10:5A:CF:3D:A9:9D:62:33:92:D1:46:AD:8C:5E:96:CE:F6:28
	 SHA256: 57:2D:8C:AA:1D:3C:8D:19:C3:93:6B:7E:D9:DE:75:63:CA:F3:33:2B:8A:12:8E:D4:52:CE:A5:25:23:32:3E:8A
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3

Extensions:

#1: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
  DNSName: tirion
]

#2: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 19 7B 9E 64 67 C2 E9 33   C4 8B 44 71 7A 1D B2 8E  ...dg..3..Dqz...
0010: 22 A7 6C E4                                        ".l.
]
]

*******************************************
*******************************************

Deployment to the client

At this point, the file, client-truststore.jks is ready to be deployed to a client. We have already discussed two; here are the details.

Appendix: list of console commands

—for quick copying and pasting.

keytool -genkeypair -keyalg RSA -keysize 2048 -validity 365 -dname "CN=tomcat" -ext san=dns:tirion -alias tomcat -keystore tomcat.jks -storepass changeit -keypass changeit
keytool -list -v -keystore tomcat.jks -storepass changeit
vim     /opt/tomcat/conf/server.xml
openssl s_client -connect tirion:8443 -showcerts > client.crt
keytool -importcert -file client.crt -alias tomcat -keystore client-truststore.jks -keypass changeit -storepass changeit -noprompt
keytool -list -v -keystore client-truststore.jks -storepass changeit

Generate Tomcat a new keystore with certificate and key inside.
Verify Tomcat's new keystore.
Add Connector for 8443.
Get Tomcat's certificate "live."
Import that certificate into a keystore (client's trust store).
Verify our client's trust store.