12

Once I obtain the certificate, how do I generate a JKS key store from it?

How do I configure the Play application to use this key store?

Anything else I need to do?

Jan
  • 331
  • 1
  • 12

3 Answers3

16

Here is a script to obtain (update) the letsencrypt certificate:

#!/bin/bash

/path/to/your/app/stop # stop the play application; especially if it is running on port 80 otherwise the certificate generation will fail

rm -rf /etc/letsencrypt.bak

mv /etc/letsencrypt /etc/letsencrypt.bak

./letsencrypt-auto certonly --standalone -n -m email@example.com --agree-tos -d example.com -d www.example.com

cd /etc/letsencrypt/live/example.com

openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out cert_and_key.p12 -CAfile chain.pem -caname root -passout pass:your_password

keytool -importkeystore -srcstorepass your_password -destkeystore keyStore.jks -srckeystore cert_and_key.p12 -srcstoretype PKCS12 -storepass your_password

/path/to/your/app/start # start the application

You can schedule a cron job to run this script periodically as letsencrypt certificates currently expire after 90 days.

Once you obtain the certificate you need to modify the application start script as follows:

/path/to/your/app/app_name_script -Dhttps.port=443 -Dplay.server.https.keyStore.path=/etc/letsencrypt/live/example.com/keyStore.jks -Dplay.server.https.keyStore.password=your_password -Djdk.tls.ephemeralDHKeySize=2048 -Djdk.tls.rejectClientInitiatedRenegotiation=true # ... more parameters if required

Nearly there. When you run the application you get A- rating from SSL Labs. The rating downgrade is related to the Forward Secrecy. In order to sort out the Forward Secrecy issue (and get a full A rating) you need to specify the order of the cipher suites by implementing a custom SSLEngineProvider:

package controllers

import java.nio.file._
import java.security.KeyStore
import javax.net.ssl._

import play.core.ApplicationProvider
import play.server.api._

class CustomSslEngineProvider(appProvider: ApplicationProvider) extends SSLEngineProvider {

  val priorityCipherSuites = List(
    "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
    "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
    "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA")


  def readPassword(): Array[Char] = System.getProperty("play.server.https.keyStore.password").toCharArray

  def readKeyInputStream(): java.io.InputStream = {
    val keyPath = FileSystems.getDefault.getPath(System.getProperty("play.server.https.keyStore.path"))
    Files.newInputStream(keyPath)
  }

  def readKeyManagers(): Array[KeyManager] = {
    val password = readPassword()
    val keyInputStream = readKeyInputStream()
    try {
      val keyStore = KeyStore.getInstance(KeyStore.getDefaultType)
      keyStore.load(keyInputStream, password)
      val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
      kmf.init(keyStore, password)

      kmf.getKeyManagers
    } finally {
      keyInputStream.close()
    }
  }

  def createSSLContext(): SSLContext = {
    val keyManagers = readKeyManagers()
    val sslContext = SSLContext.getInstance("TLS")
    sslContext.init(keyManagers, Array.empty, null)
    sslContext
  }

  override def createSSLEngine(): SSLEngine = {
    val ctx = createSSLContext()
    val sslEngine = ctx.createSSLEngine
    val cipherSuites = sslEngine.getEnabledCipherSuites.toList
    val orderedCipherSuites =
      priorityCipherSuites.filter(cipherSuites.contains) ::: cipherSuites.filterNot(priorityCipherSuites.contains)
    sslEngine.setEnabledCipherSuites(orderedCipherSuites.toArray)
    val params = sslEngine.getSSLParameters
    params.setUseCipherSuitesOrder(true)
    sslEngine.setSSLParameters(params)
    sslEngine
  }
}

Do not forget to set

play.server.https.engineProvider=controllers.CustomSslEngineProvider

in your application.conf.

Tested with Play 2.5.x

Jan
  • 331
  • 1
  • 12
  • 1
    It's not a good idea to "mv /etc/letsencrypt". It may destroyed others certificates and archives. If you do want to use a clean dir, you can use the --config-dir option : http://letsencrypt.readthedocs.io/en/latest/using.html – Tom Jul 13 '16 at 10:12
  • Sure. I've got just one certificate so I don't care. This answer is rather about configuring Play and not about managing certificates. Feel free to modify the sample script as you wish. – Jan Jul 13 '16 at 13:58
  • Incredibly helpful answer. Only a little bit outdated. I had to do this just now, here's my file: https://stackoverflow.com/a/62659945/49153 – Ali Jun 30 '20 at 14:37
5

I searched in various forums, in the end I came up with a very fast (and almost automated) solution: First, as on the letsencrypt website they suggest, run these:

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot 

then run

sudo certbot certonly --standalone -d domain.name.com

(for wildcards it's a bit more complicated, but the remainder of this procedure should be the same)

at this point it should tell you where the keys are, on some directory like /etc/letsencrypt/live/domain.name.com/*.pem (three keys: fullchain, privkey, and a chain)

then run openssl (editing domain name and password)

sudo openssl pkcs12 
        -export -in /etc/letsencrypt/live/domain.name.com/fullchain.pem 
        -inkey /etc/letsencrypt/live/domain.name.com/privkey.pem 
        -out cert_and_key.p12 
        -CAfile /etc/letsencrypt/live/domain.name.com/chain.pem 
        -caname root 
        -passout pass:<insert some password here>

then keytool (editing keystore path and password)

sudo keytool 
  -importkeystore 
  -srcstorepass <the password you inserted above>
  -destkeystore <path/key>.jks 
  -srckeystore cert_and_key.p12 
  -srcstoretype PKCS12 
  -storepass <the password you inserted above>

and finally you should find the jks key on the path you wrote above.

In application.conf:

play.server.https.keyStore.path = "<path/key>.jks"
play.server.https.keyStore.type = "JKS"
play.server.https.keyStore.password = "<the password you inserted above>"

Tested with Play 2.6.15, on Ubuntu 16 and 18

LowFieldTheory
  • 1,722
  • 1
  • 26
  • 39
  • 1
    great, i have apache CDN and playframework, i added letsencrypt for apache and then configure this part and working well with play2.5 – cyril Apr 08 '19 at 07:26
1

I recently had to do this, here's my file:

#!/usr/bin/env bash

sudo killall java #stop the application gracefully

rm -rf /etc/letsencrypt.bak

cp -r /etc/letsencrypt /etc/letsencrypt.bak

certbot renew --standalone

cd /etc/letsencrypt/live/example.com/

openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out cert_and_key.p12 -CAfile chain.pem -caname root -passout pass:your_password

keytool -importkeystore -srcstorepass your_password -destkeystore keyStore.jks -srckeystore cert_and_key.p12 -srcstoretype PKCS12 -storepass your_password

After this, you'll need to set the properties when running the app using the same format as the accepted answer:

sudo /path/to/app -Dhttp.port=80 -Dhttps.port=443 -Dplay.server.https.keyStore.path=/etc/letsencrypt/live/api.ali.actor/keyStore.jks -Dplay.server.https.keyStore.password=your_password -Djdk.tls.ephemeralDHKeySize=2048 -Djdk.tls.rejectClientInitiatedRenegotiation=true
Ali
  • 261,656
  • 265
  • 575
  • 769