Two Ways to Integrate an SSL Certificate Into a Web Site

29 Jun 2022

Prerequisites

This assumes:

  1. I have already created a private key myapp.org.key:
1
openssl genrsa -out myapp.org.key 2048
  1. I have used my key to create a CSR:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
openssl req -new -newkey rsa:2048 -nodes \
  -key myapp.org.key \
  -out myapp.csr

Country Name:         [whatever]
State:                [whatever]
Locality:             [whatever]
Organization Name:    [whatever]
Organizational Unit:  [whatever]
Common Name:          www.myapp.org
Email Address:        .

A challenge password: <LEAVE BLANK>
An optional company:  <LEAVE BLANK>
  1. I have used the CSR to request a cert, performed the DNS validation, and received the certificate from the certificate provider (not Let’s Encrypt in this case).

The certificate file is www_myapp_org.crt and in my case is in the PEM format.

Tomcat Configuration

For Tomcat, I do not use the additional certificates which create the full certificate chain - but, if needed, these can be concatenated to the end of the certificate file (after the certificate for my site, which must come first in the file).

My server.xml connectors:

XML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<Connector port="80" protocol="HTTP/1.1"
           URIEncoding="UTF-8"
           connectionTimeout="20000"
           proxyName="www.myapp.org"
           redirectPort="443" />

<Connector port="443" SSLEnabled="true"
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150" scheme="https" secure="true"
           proxyName="www.myapp.org"
           URIEncoding="UTF-8"
           compression="on"
           compressionMinSize="2048"
           noCompressionUserAgents="gozilla, traviata"
           compressibleMimeType="text/html,text/xml,text/plain,text/javascript,text/css,application/json" >
    <SSLHostConfig certificateVerification="none"
                   sslProtocol="TLS" >
        <Certificate certificateFile="conf/www_myapp_org.crt"
                     certificateKeyFile="conf/myapp.org.key" />
    </SSLHostConfig>
    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol"
                     compression="on"
                     compressionMinSize="2048">
    </UpgradeProtocol>
</Connector>

My web.xml security constraint:

XML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Protected Context</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

Jetty Configuration

In this case, I create a Java keystore containing the server private key and the full certificate chain (my cert comes first in the cert file).

This is a 2-step process:

Step 1: create a PKCS-12 keystore file containing the key and certs:

1
2
3
4
5
6
7
openssl pkcs12 -export \
    -in www_myapp_org.crt \
    -inkey myapp.org.key \
    -name myapp.org \
    -out myapp-PKCS-12.p12

Enter Export Password: [p/w here]

Step 2: Import the above .p12 keystore file into a new Java .jks keystore file using the Java keytool command:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
keytool -importkeystore \
    -srckeystore myapp-PKCS-12.p12 \
    -srcstoretype PKCS12 \
    -destkeystore myapp.jks

Importing keystore myapp-PKCS-12.p12 to myapp.jks...
Enter destination keystore password: [p/w here]
Enter source keystore password: [p/w here]
Entry for alias myapp.org successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled

(Maybe you can do this in one step but I did not figure that out. I needed to create the PKCS 12 file first using openssl, before creating the Java keystore using keytool.)

UPDATE:

It turns out that from Java 9 onwards, you can use the PKCS 12 file (from step 1 above) directly, without needing to perform step 2.

From the keytool documentation:

In JDK 9 and later, the default keystore implementation is PKCS12.

The proprietary Java Keystore format (.jks) can still be used.

END of UPDATE

My Jetty is embedded and I configure it programmatically, in my case (not via XML):

Java
1
2
3
4
5
6
7
import org.eclipse.jetty.util.ssl.SslContextFactory;

...

SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(props.getProperty("myapp.keystore.path"));
sslContextFactory.setKeyStorePassword(props.getProperty("myapp.keystore.pass"));