Smile CDR v2024.08.PRE
On this page:

38.1.1TLS and HTTPS

 

This page describes how to configure Smile CDR to use TLS (aka SSL) on an HTTP server.

38.1.2Background

 

If you are already comfortable with how TLS works, please feel free to skip to the next section.

The HTTPS protocol uses an encryption standard called TLS (formerly called SSL). While there are nuances to these terms, you can think of them all as being interchangeable. TLS is based on public/private key cryptography. You can think of this type of cryptography as being a bit like a physical lock and key used to open a door, where the server has a key and the client has a lock that the server needs to open.

This analogy is a bit strange, but it works: In the real world, we use a lock on the door to make sure that only the right person (the one holding the key) is allowed to enter the building. Of course the lock is not private: Anyone can look at the lock. The key however needs to be kept private since anyone who gets a good look at it can make a copy and get access to the building. A web server using HTTPS is of course concerned with encryption (this is the primary purpose of HTTPS) but this protocol also serves a second less obvious purpose: It allows the client to be sure they are talking to the right server. The problem here makes sense: If a user is connecting to their bank server (or their health records!) they are going to enter their credentials and other sensitive information. If they have connected to a server that is impersonating their bank, it doesn't matter that the connection is encrypted since they will still send the same credentials. So TLS needs to allow the client to trust that they have connected to the right server.

This is where the lock and key analogy comes in. The server (the bank) has a key that belongs only to it. The client (the user) has a lock that this key can open. When the client is about to give their credentials to the bank, they will first say "show me that your key works in my lock" and if it works we have trust.

In the virtual world, the key is called a "private key" and the lock is a "public certificate". Your server has a private key that belongs only to it, and your client has a public certificate that can only be validated by someone who holds the corresponding private key.

You may be wondering: how can the server expect a client that it has never met before to magically have a certificate (the lock) that it can pair with its private key (the key).

The answer to this question depends on the fact that both parts are created at the same time in a single operation. This called Generating a Keypair. There are two ways of going about this:

  1. You can use a private key that was issued by a trusted third party company, including LetsEncrypt as well as a number of private/commercial providers.

    The advantage to this approach is that clients (such as web browsers, as well as tools like curl/wget and the HAPI FHIR client) come with a set of built-in certificates for these well known providers.

    For simplicity here, we are skipping over an important concept called "certificate chaining", but the important part is this: If you use a well-known provider to generate your keypair, you can be assured that your clients probably come with an appropriate certificate pre-installed.

  2. You can generate your own "keypair", which is a private key and a corresponding certificate. This is commonly known as "self-signing".

    This approach is completely secure and can be done with freely available tools, but it has one disadvantage: The certificate that the client needs in order to verify the server's key will not be pre-installed on your client.

    This means that you will need to explicitly supply the certificate to any clients via configuration.

38.1.2.1File Formats

Private keys and certificates are stored for use by Smile CDR in special files called KeyStore files. There are four formats of KeyStore that are supported by Smile CDR:

  • Java KeyStore File (JKS): The Java KeyStore file uses the extension .jks and was the only acceptable way to store keys for Java applications for a long time. These files are generally managed using the Java Keytool command line utility.

  • PKCS#12 File (P12): The PKCS#12 file uses the extension .p12. PKCS#12 is a standard for container files that is supported by a wide variety of tools, including Smile CDR.

    Note that while JKS files are still supported and are perfectly acceptable to use, it is generally recommended to use PKCS#12 files instead for new applications, since PKCS#12 is a standard

  • PEM File: The PEM (Privacy Enhanced Mail) file uses the extension .pem. A single key and/or certificate can also be transported in a PEM file. PEM files do not have an associated password, unlike JKS and P12 files.

  • JSON Web KeyStore (JWKS) File: The JWKS file uses the extension .jwks. JWKS files use a text-based JSON encoding to represent keys and certificates. They are used for keys and certificates associated with OpenID Connect clients and servers.

Within a KeyStore file, each key or certificate will be given an ID or Alias, which is just a string used to identify an individual key within a KeyStore. In this context these two words are synonymous: JKS files generally refer to the key alias where other files use the word ID.

38.1.2.2TLS Client Authentication

TLS Client Authentication, sometimes called Two-Way TLS or TLS Mutual Authentication builds on the approach above by adding an additional Keypair.

With standard TLS, the client is able to trust that the server is who it claims to be. This is good for the client, but does not provide any assurance to the server that the client is who they claim to be.

When using TLS Client Authentication, the server has a private key and the client has a corresponding certificate (as explained above), but the client also has a (different) private key and the server has a certificate corresponding to this key.

In this way, the client can verify the identity of the server (by verifying the server key) and the server can verify the identity of the client (by verifying the client key).

38.1.2.3Smile CDR Configuration: KeyStores and TrustStores

In Smile CDR, several configuration items can be used to provide keys and certificates to various modules:

  • KeyStore File: The KeyStore File holds private keys. For HTTP Servers, this allows the server to support standard TLS. For HTTP Clients, ths allows the client to connect to a server requiring TLS Client Authentication.

  • TrustStore File: The TrustStore file holds public certificates. For HTTP Servers, this file contains certificates for any clients that will connect to the server using TLS Client Authentication. For HTTP Clients, this file contains certificates for any self-signed keys used by servers.

38.1.3Creating a Self-Signed KeyStore

 

Use the following command to create a KeyStore containing a key with the alias server:

keytool -genkey -keyalg RSA -alias server -keystore keystore.p12 -storepass changeit -storetype pkcs12 -validity 360 -keysize 4096

This will produce the following output. User input is shown in red. Note that the "First and Last Name" question corresponds to the CN (common name) attribute in the key. This should generally be the domain name for the server where this key will be used in the case of HTTPS servers:

What is your first and last name?
[Unknown]: fhirserver.example.com
What is the name of your organizational unit?
[Unknown]: FHIR Servers Department
What is the name of your organization?
[Unknown]: Exciting Tech Co
What is the name of your City or Locality?
[Unknown]: Toronto
What is the name of your State or Province?
[Unknown]: ON
What is the two-letter country code for this unit?
[Unknown]: CA
Is CN=fhirserver.example.com, OU=FHIR Servers Department, O=Exciting Tech Co, L=Toronto, ST=ON, C=CA correct?
[no]: yes

38.1.3.1Exporting the Certificate

To export the public certificate from the newly generated keypair file:

openssl pkcs12 -in keystore.p12 -clcerts -nokeys -out truststore.pem

This will produce the following output. User input is shown in red.

Enter Import Password: changeit
MAC verified OK

38.1.4Importing a LetsEncrypt Certificate into a KeyStore

 

The following steps show how to create a KeyStore using a private key and chain obtained from LetsEncrypt. Note that you will need to replace "[hostname]" with your actual LetsEncrypt hostname in several places.

# Export the key into a PKCS12 file
openssl pkcs12 -export -in /etc/letsencrypt/live/[hostname]/cert.pem -inkey /etc/letsencrypt/live/[hostname]/privkey.pem -out keystore.p12 -name [hostname] -CAfile /etc/letsencrypt/live/[hostname]/chain.pem -caname root -password pass:changeit

38.1.5Selecting Ciphers and Protocol

 

TLS-enabled servers may optionally be configured with a whitelist or a blacklist for both ciphers and protocol.

This can be used to restrict specific ciphers, or ban old versions of TLS/SSL.

To see a list of available ciphers, consult your JDK documentation. The following links may be useful:

38.1.5.0.1Optimal Security

If possible, consider setting a protocol whitelist of TLSv1.3, as this ensures that all clients are required to connect using the most modern revision of the TLS specification. Note that this may prevent older clients from being able to connect. See CanIUse TLS 1.3 for a rundown of browser support.

38.1.5.0.2Using TLS 1.0

The TLS 1.0 protocol is generally not recommended for use as it has known vulnerabilities that have been addressed in newer versions of the TLS specification. However, the following configuration is known to work in forcing the use of TLS 1.0 (e.g. to interact with a JDK 6 application that can not be upgraded).

  • Protocol Whitelist: TLSv1
  • Cipher Whitelist: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA

38.1.6Enabling TLS Mutual Authentication (Client Auth)

 

38.1.6.1Creating a Client KeyPair

If the client does not already have a certificate it can use to identify itself, the following steps may be followed by the client.

38.1.6.1.1Step 1: Generate a Certificate Authority Root

Create the server key. Note that this file should be generated by the client, and kept secure.

openssl genrsa -out ca.key 4096

This command will produce output similar to the following:

Generating RSA private key, 4096 bit long modulus
....................................................................++
e is 65537 (0x10001)

Next, generate a certificate. During this step you will be asked for information about your organization.

openssl req -new -x509 -days 365 -key ca.key -out ca.crt

This command will prompt for some details about the organization making the request.

You are about to be asked to enter information that will be incorporated into your certificate request.

Country Name (2 letter code) []: US
State or Province Name (full name) []: OH
Locality Name (eg, city) []: Springfield
Organization Name (eg, company) []: Acme
Organizational Unit Name (eg, section) []: Widgets
Common Name (eg, fully qualified host name) []: example.com
Email Address []: info@example.com

38.1.6.1.2Step 2: Generate a Client Key

This step generates a certificate that is specific to the client application making requests. First, we generate a key.

openssl genrsa -out client.key 4096

Next, generate a signing request.

openssl req -new -key client.key -out client.csr

This will produce output similar to the following. Note that you must not use the exact same answers to all questions that you provided when creating the Certificate Authority Root above.

You are about to be asked to enter information that will be incorporated into your certificate request.

Country Name (2 letter code) []: US
State or Province Name (full name) []: OH
Locality Name (eg, city) []: Springfield
Organization Name (eg, company) []: Acme
Organizational Unit Name (eg, section) []: Widget Client
Common Name (eg, fully qualified host name) []: example.com
Email Address []:info@example.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []: (hit enter with no value)

Next, generate a signed certificate.

openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt

This will produce output similar to the following:

Signature ok subject=/C=US/ST=OH/L=Springfield/O=Acme/OU=Widget Client/CN=example.com/emailAddress=info@example.com Getting CA Private Key

Optionally, you may now verify that the certificate works:

openssl verify -verbose -CAfile ca.crt client.crt

Finally, generate a PKCS#12 KeyStore that can be imported into Smile CDR. Note that the KeyStore password of changeit is used below. You may choose to use a different password. When this command has completed, you will have a file called truststore.p12.

keytool -import -alias acme -file client.crt  -keystore truststore.p12 -storepass changeit -storetype pkcs12

This will produce output similar to the following.

Owner: EMAILADDRESS=info@example.com, CN=example.com, OU=Widget Client, O=Acme, L=Springfield, ST=OH, C=US
Issuer: EMAILADDRESS=info@example.com, CN=example.com, OU=Widgets, O=Acme, L=Springfield, ST=OH, C=US
Serial number: 1
Valid from: Thu Dec 06 09:08:08 EST 2018 until: Fri Dec 06 09:08:08 EST 2019
Certificate fingerprints:
   SHA1: C9:4A:A1:D2:F3:F9:54:04:5A:3A:69:45:14:B0:19:BC:26:63:D4:77
   SHA256: 4A:32:72:7B:C2:9D:D4:EE:A3:98:61:9D:8A:9C:9F:18:1B:98:CE:87:2A:90:F3:B7:7D:77:79:E2:35:6B:F5:E3
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 1
Trust this certificate? [no]: yes
Certificate was added to keystore

38.1.6.1.3Step 3: Generate a Client KeyStore

The following command creates a private PKCS#12 KeyStore containing both the key and certificate. This file will be needed by your client in order to invoke the service.

openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12

This command will produce output similar to the following:

Enter Export Password: changeit
Verifying - Enter Export Password: changeit

38.1.6.1.4Step 5: Install Truststore in Smile CDR

In your endpoint module, set the following settings:

Client Authentication yes
TLS TrustStore Filename file:///path/to/cacerts.p12
TLS TrustStore Password Password from truststore above

38.1.6.1.5Step 4: Test

The following example shows a cURL command being used to present the generated client key to the server.

curl --cert client/client.crt --key client/client.key https://localhost:8000

38.1.7Programmatically Configuring a FHIR Endpoint KeyStore

 

TLS-enabled FHIR endpoints allows for supplying a custom Java KeyStore through theServer Interceptor Framework.

To provide a custom Java KeyStore, follow these steps:

  1. Enable TLS support with endpoint propertytls.enabled

  2. Provide a KeyStore password with the FHIR Endpoint configuration propertytls.keystore.keypass

  3. Provide a business specific interceptor implementation annotated with@CdrHook on the pointcut CdrPointcut.SERVER_CONFIGURATION_KEYSTORE

  4. Register the interceptorclass with Smile CDR

Example:

@CdrHook(CdrPointcut.SERVER_CONFIGURATION_KEYSTORE)
public KeyStore provideKeystore(String theKeystorePassword) throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {

    InputStream keystoreInputStream = getInputStream();

    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(keystoreInputStream, theKeystorePassword.toCharArray());

    return keyStore;
}

See the cdr-interceptor-starterproject for a working example.

38.1.8Smile Util TLS JSON Authentication File

 

There are some Smile Util commands that make HTTP requests to FHIR REST endpoints.

If the endpoints are secured (i.e. use HTTPS), then a TLS JSON authentication file must be provided in order authenticate the client and / or server.

The JSON authentication file should have the following structure:

{
    "keyStore": {
        "filePath" : "file:///path-to-keystore-file.p12",
        "storePass": "somePassword",
        "keyPass": "somePassword",
        "alias": "someAlias"
    },
    "trustStore": {
        "filePath" : "file:///path-to-truststore-file.p12",
        "storePass": "somePassword",
        "alias": "someAlias"
    }
}
  • keyStore – Information about the client's KeyStore that will be used by the server to authenticate the HTTPS request.

  • keyStore.filePath – File path to the client's KeyStore. This can be in the format classpath:path/to/file.p12 or file:///path/to/file.p12. Valid file extensions are .jks (Java Keystore) or .p12 (PKCS#12 store).

  • keyStore.storePass – Password for the KeyStore. If the value supplied is "PROMPT", Smile Util will prompt the user to enter a password interactively.

  • keyStore.keyPass – Password for the key inside the KeyStore. If the value supplied is "PROMPT", Smile Util will prompt the user to enter a password interactively.

  • keyStore.alias – Alias of the key inside the KeyStore.

  • trustStore – Information about the client's TrustStore that will be used by the client to authenticate the HTTPS response.

  • trustStore.filePath – File path to the client's TrustStore. This can be in the format classpath:path/to/file.p12 or file:///path/to/file.p12. Valid file extensions are .jks (Java Keystore) or .p12 (PKCS#12 store).

  • trustStore.storePass – Password for the TrustStore. If the value supplied is "PROMPT", Smile Util will prompt the user to enter a password interactively.

  • trustStore.alias – Alias of the certificate inside the TrustStore.