More notes on digital certificates

July 2022
last update:

...especially as concerns Tomcat.

Links


Generating a self-signed certificate...

You don't have to submit a CSR to a commercial certificate authority (CA) if you don't need such an authority chain. Why? Because you're doing something internal and don't care about a formal, commercial authority chain.

We can create a self-signed certificate using openssl. I used what's at this hyperlink plus what's under the heading Import Signed/Root/Intermediate Certificate down the page of Java keytool Essentials Working with Java Keystores.

It's likely the case you can use keytool to roll the whole enchilada, but I failed, got impatient and used openssl which seemed easier to me.

First, create both the private key and the certificate signing request (CSR) with a single command:

russ@tirion ~ $ openssl req -newkey rsa:2048 -keyout temp.key -out temp.csr
Generating a RSA private key
.................+++++
............................................................................................+++++
writing new private key to 'temp.key'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:UT
Locality Name (eg, city) []:Provo
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Wind of Keltia
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:windofkeltia.com
Email Address []:[email protected]

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

russ@tirion ~ $ ll
-rw-------   1 russ russ      1854 Jul  8 16:06  temp.key
-rw-rw-r--   1 russ russ      1050 Jul  8 16:07  temp.csr

Next, create a self-signed certificate—a certificate that's signed with its own private key. It works fine, but sometimes software using it will display a disconcerting warning to the effect that the certificate isn't trusted. Also, this certificate will expire after a year:

russ@tirion ~ $ openssl x509 -signkey temp.key \
>                            -in temp.csr      \
>                            -req -days 365      \
>                            -out temp.crt
Signature ok
subject=C = US, ST = UT, L = Provo, O = Wind of Keltia, CN = windofkeltia.com, emailAddress = [email protected]
Getting Private key
Enter pass phrase for temp.key:password

russ@tirion ~ $ ll
-rw-------   1 russ russ      1854 Jul  8 16:06  temp.key
-rw-rw-r--   1 russ russ      1050 Jul  8 16:07  temp.csr # (don't care about this file from now on)
-rw-rw-r--   1 russ russ      1310 Jul  8 16:16  temp.crt

Now get the above into a keystore for use by Tomcat...

This should give us temp.crt which we can import using keytool. keytool wants an X.509 certificate which is what temp.crt is:

russ@tirion ~ $ keytool -importcert \
>                       -trustcacerts -file temp.crt \
>                       -alias tomcat \
>                       -keystore keystore.jks
Enter keystore password: password
Re-enter new password: password
Owner: [email protected], CN=windofkeltia.com, O=Wind of Keltia, L=Provo, ST=UT, C=US
Issuer: [email protected], CN=windofkeltia.com, O=Wind of Keltia, L=Provo, ST=UT, C=US
Serial number: e536cc416b7c8a8e43fcc628bad913bbd2b2a41
Valid from: Fri Jul 08 16:16:12 MDT 2022 until: Sat Jul 08 16:16:12 MDT 2023
Certificate fingerprints:
	 SHA1: 86:33:80:ED:98:48:21:DF:5D:EA:BA:AE:1E:07:6A:B8:8D:06:04:D5
	 SHA256: 5E:6E:7D:78:F3:C3:01:6F:67:41:0D:6F:74:AA:49:10:DA:39:9E:B5:23:B0:DB:B1:7B:1A:B8:1F:A4:99:75:37
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 1
Trust this certificate? [no]: yes
Certificate was added to keystore

russ@tirion ~ $ ll
-rw-------   1 russ russ      1854 Jul  8 16:06  temp.key
-rw-rw-r--   1 russ russ      1050 Jul  8 16:07  temp.csr   # (don't care about this file from now on)
-rw-rw-r--   1 russ russ      1310 Jul  8 16:16  temp.crt
-rw-rw-r--   1 russ russ      1287 Jul  8 16:46  keystore.jks

Apache Tomcat over TLS/SSL

See How to Set Up HTTPS SSL on Tomcat.


Integrating Tomcat with the certificate...

This means, "How to get keystore.jks (created above) into Tomcat?

  Please note that a JKS keystore (and/or truststore) are always and only ever files in your filesystem.

Translate the certificate

If you have an existing certificate (created by openssl) into a JKS keystore, you must remove any comments in the certificate put there openssl before attempting to import the certificate via keytool.

The following command creates a PKCS12 certificate, suitable for use with keytool, from the key, certificate and certificate signing request illustrated in the previous collection of notes, Generating a self-signed certificate.

russ@tirion ~ $ openssl pkcs12 -export -in temp.crt \
>                              -inkey temp.key      \
>                              -out temp.p12 tomcat \
>                              -CAfile temp.crt     \
>                              -caname root -chain

Prepare the certificate keystore

Tomcat operates only on JKS-, PKCS11- or PKCS12 format keystores. The former is Java's standard format and what's created by the keytool command-line utility. The latter is an Internet standard and is manipulated by OpenSSL.

russ@tirion ~ $ ${JAVA_HOME}/bin/keytool -genkeypair -alias tomcat -keyalg RSA

Note, ...

  1. While doing this, you'll be prompted for the keystore password. The default password used by Tomcat is "changeit," though you can specify something else (and note that change in ${CATALINA_BASE}/conf/server.xml).
  2. Next, you'll be prompted for information about this certificate, such as "company," "contact name," etc. This will be displayed to users attempting to access a secure page in the application; expectations and your mileage may vary here.
  3. Last, you'll be prompted for the key password, which is the password specifically for this certificate (as opposed to other certificates already in the same keystore or that you'll add later).
  4. This places the new key on the path ~/.keystore. To specify a different location including filename, use the -keystore option:
    russ@tirion ~ $ ${JAVA_HOME}/bin/keytool -genkeypair -alias tomcat -keyalg RSA -keystore keystore-path
    

At this point, you should have a keystore file containing a certificate that can be used by Tomcat.


Configuring Tomcat's server.xml...

Which implementation?

Tomcat can use three different SSL implementations.

  1. Apache Portable Runtime (APR) native library for Tomcat is used to optimize Tomcat performance. This is by far the best practice to follow.
    <dependency>
      <groupId>org.apache.apr</groupId>
      <artifactId>apr</artifactId>
      <version>1.5.2</version>
      <type>pom</type>
    </dependency>
    
  2. Java Secure Socket Extension (JSSE) provided as part of the Java runtime.
  3. JSSE that uses OpenSSL.

Tomcat's global connector options are configured in ${CATALINA_BASE}/conf/server.xml. Look under <Service name="Catalina"> near <Connector port=8443" protocol="org.apache.coyote.http11.Http11AprProtocol"... and put this, uncommented, in that vicinity:

<Connector port="8443"
        protocol="org.apache.coyote.http11.Http11AprProtocol"
        maxHttpHeaderSize="8192"
        maxThreads="150"
        enableLookups="false"
        disableUploadTimeout="true"
        acceptCount="100"
        scheme="https"
        secure="true"
        SSLEnabled="true">
  <SSLHostConfig>
    <Certificate
        certificateKeyFile="conf/localhost-rsa-key.pem"
        certificateFile="localhost-rsa-cert.pem"
        certificateChainFile="localhost-rsa-chain.pem"
        type="RSA" />
  </SSLHostConfig>
</Connector>

However, APR is relatively difficult to install and getting this into Dockerfile multiplies the complexity by an order of magnitude. So, for now, we're not going to use APR.


Accepting Tomcat with JSSE...

Creating keys and certificates...

  1. Generating a key pair...
    $ keytool -genkeypair \
    > -alias tomcat \
    > -keyalg RSA \
    > -keysize 2048 \
    > -dname "CN=windofkelti.com, O=Wind of Keltia, L=Provo, ST=UT, C=US" \
    > -keypass changeit \
    > -validity 365 \
    > -storetype JKS \
    > -keystore keystore.temp \
    > -storepass changeit
    
    Warning:
    The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 \
    which is an industry standard format using: \
    keytool -importkeystore -srckeystore keystore.tomcat -destkeystore keystore.tomcat -deststoretype pkcs12
    
  2. As the last command suggests, ...
    $ keytool -importkeystore \
    > -srckeystore keystore.temp \
    > -destkeystore keystore.tomcat \
    > -deststoretype pkcs12
    
  3. Check out our keystore...
    $ openssl pkcs12 -in keystore.tomcat -info
    Enter Import Password: changeit
    MAC: sha1, Iteration 100000
    MAC length: 20, salt length: 20
    PKCS7 Data
    Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 10000, PRF hmacWithSHA256
    Bag Attributes
        friendlyName: tomcat
        localKeyID: 54 69 6D 65 20 31 36 35 37 35 38 38 38 38 34 30 31 35
    Key Attributes: 
    Enter PEM pass phrase: changeit
    Verifying - Enter PEM pass phrase: changeit
    -----BEGIN ENCRYPTED PRIVATE KEY-----
    MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIQPvh9D4us+UCAggA
    MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECMofh3SfEKFdBIIEyARYIWivtX99
    /eenbz6z0VOrezlgB7n0tplYDHHsAbtc8KoVoN+cbzAsvSMHv//S7ZICcmhMfjDJ
    Hzh7bzQ7YGmMiybuBV9Ntc0K0TMzXnL1vtCebGVmMPfvcXHNdXz67mHs3RKugNdH
    ...
    7sZDnvSskQa/Cf4otVAVEZBIdvLcLvtaARANuCs0qiXeAvqgHMEpQggOzxdC866q
    Zr8lE0zegGAg+Z2ecXfc6+r337GpxQ5W90LmxUajXgfUmd3ybmzohI5OJWq01m9Z
    oQ1q08bDtVm600xc24yPhnXiDXbtkh08n52pVuBMzhsdjumVElMNRw6Pi5Rm3cts
    O/qLiRtDnCDGdSc2Prn0Pw==
    -----END ENCRYPTED PRIVATE KEY-----
    PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 10000, PRF hmacWithSHA256
    Certificate bag
    Bag Attributes
        friendlyName: tomcat
        localKeyID: 54 69 6D 65 20 31 36 35 37 35 38 38 38 38 34 30 31 35
    subject=C = US, ST = UT, L = Provo, O = Wind of Keltia, CN = windofkelti.com
    
    issuer=C = US, ST = UT, L = Provo, O = Wind of Keltia, CN = windofkelti.com
    
    -----BEGIN CERTIFICATE-----
    MIIDWTCCAkGgAwIBAgIEIATpDzANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJV
    UzELMAkGA1UECBMCVVQxDjAMBgNVBAcTBVByb3ZvMRcwFQYDVQQKEw5XaW5kIG9m
    IEtlbHRpYTEYMBYGA1UEAxMPd2luZG9ma2VsdGkuY29tMB4XDTIyMDcxMjAxMjAz
    NloXDTIzMDcxMjAxMjAzNlowXTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMQ4w
    .
    PQhzRF0MMRV2cvgkr1lwAy+XOz7nzfAxzN6Lcnz2Crdu61NOuqR6G85KPOF82/O9
    mTxNVOw6854uXrLncEsFcBMaOd6MfYzfVt5COE94ehTCHw7aSzLEH7zEvHY1Cik1
    K2E6web4+/wJmEnASH5rE6OrCL01PS6j9G0o7xNMZmw3/BffJlnQDUM8QDjF
    -----END CERTIFICATE-----
    
  4. We're going to accept...

    Java Secure Socket Extension (JSSE)


    ...provided as part of the Java runtime for there is nothing else to do in Dockerfile than we have already done.

    <Connector port="8444"
            protocol="HTTP/1.1"
            SSLEnabled="true"
            maxThreads="150"
            scheme="https"
            secure="true"
            clientAuth="false"
            sslProtocol="TLS">
      <SSLHostConfig>
        <Certificate
            certificateKeyFile="conf/keystore.tomcat"
            certificateFile="keystore.tomcat"
            type="RSA" />
      </SSLHostConfig>
    </Connector>
    

How to install APR in Tomcat...

APR support requires three, main native components:

  1. the APR library (JAR)
  2. JNI wrappers for APR used by Tomcat (libtcnative—must be compiled.) Requirements:
    • APR 1.2+ development headers (libapr1-dev package)
    • OpenSSL 1.0.2+ development headers (libssl-dev package)
    • JNI headers from Java compatible JDK 1.4+
    • GNU development (gcc, make)
  3. OpenSSL libraries
  4. CATALINA_OPTS="${CATALINA_OPTS} -Djava.library.path=/usr/local/apr/lib"

How to test...
  1. Restart Tomcat.
  2. Connect to your application using a URL such as
    https://hostname:8443/application-name/etc.
    

Questions...

  1. How to create self-signed certificates that don't lead to warning caveats when used?
  2. Build the APR JAR into the application? or add to Tomcat?
  3. Is there any way to deny HTTP in Docker deployment while tolerating it in WAR deployment?

    It's possible to configure two virtual hosts, one for HTTP and one for HTTPS, to configure to the Tomcat servlets.


Geek Flare tutorial...

The GeekFlare tutorial...

Note that, below, we need a Tomcat we can screw up plus a working application to run, such as the one described here— simple-webapp.

  1. Generate a CSR... (we don't care, but we do need a key and certificate)
    $ keytool -genkeypair \
    > -alias tomcat \
    > -keyalg RSA \
    > -keysize 2048 \
    > -dname "CN=windofkelti.com, O=Wind of Keltia, L=Provo, ST=UT, C=US" \
    > -keypass changeit \
    > -validity 365 \
    > -storetype JKS \
    > -keystore keystore.temp \
    > -storepass changeit
    
    Warning:
    The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 \
    which is an industry standard format using: \
    keytool -importkeystore -srckeystore keystore.tomcat -destkeystore keystore.tomcat -deststoretype pkcs12
    $ keytool -importkeystore \
    > -srckeystore keystore.temp \
    > -destkeystore keystore.tomcat \
    > -deststoretype pkcs12
    
  2. Import the new certificate into a keystore file...

    We just did this, I think.

  3. Enable SSL in Tomcat...
    1. Go to /opt/tomcat/conf/server.xml.
    2. Look for <Connector port="8080" protocol="HTTP/1.1"....
    3. Add these lines to it:
            SSLEnabled="true"
            scheme="https"
            keystoreFile="keystore.tomcat"
            keystorePass="changeit"
            clientAuth="false"
            sslProtocol="TLS"
      
  4. Change Tomcat to listen on port 443...

    ...cause ain't no way we're going to leave 8080 as the secure port—too confusing. So, over 8080 at the line we found above, replace it with 443. Our final server.xml configuration should appear thus:

    <Connector port="443" protocol="HTTP/1.1"
               connectionTimeout="20000"
               SSLEnabled="true"
               scheme="https"
               keystoreFile="ssl/bloggerflare.jks"
               keystorePass="chandan"
               clientAuth="false"
               sslProtocol="TLS"
               redirectPort="8443" />
    
  5. Test Tomcat for SSL use...

    Go to https://windofkeltia.com/.

  6. Test for vulnerability. This step isn't useful here in these notes, but it is worth checking into.

Putting an SSL certificate into a Java keystore...

From Iowa State University.

Note that IBM's Java will not work for this. Also, you might give the CSR a little better name than cert-name.csr for this, as well as the other key- and certificate filenames.

Create a new key...

$ openssl genrsa -out cert-name.key 4096

Create a certificate signing request (CSR)...

$ openssl req -new -key cert-name.key \
>             -out      cert-name.csr

Send the CSR to a certification authority for signing. Once signed, your cert-name.csr will come back as cert-name.crt and the certificate chain as cert-name.chain.crt.

Create the Java keystore...

  1. Change the x.509 certificate to PFX...
    $ openssl pkcs12 -export
    >                -out    cert-name.pfx
    >                -in     cert-name.crt
    >                -inkey  cert-name.key
    >                -name   cert-name
    >                -CAfile cert-name.chain.crt
    >                -noiter
    >                -nomaciter
    
  2. Import the PFX file to a new Java keystore file...
    $ keytool -importkeystore
    >         -srckeystore  cert-name.pfx
    >         -destkeystore cert-name.jks
    >         -deststoretype JKS