How to configure a Play application to use Let's Encrypt certificate?
Asked Answered
S

3

13

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?

Subadar answered 12/7, 2016 at 22:40 Comment(1)
Please check this post ravinderpayal.com/Free-SSL-Certificate-play-framework It's very detailed.Subsonic
S
17

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 protected] --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

Subadar answered 12/7, 2016 at 22:40 Comment(3)
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 : letsencrypt.readthedocs.io/en/latest/using.htmlEunuchoidism
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.Subadar
Incredibly helpful answer. Only a little bit outdated. I had to do this just now, here's my file: https://mcmap.net/q/856853/-how-to-configure-a-play-application-to-use-let-39-s-encrypt-certificateBufordbug
Q
6

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

Quijano answered 14/10, 2018 at 19:16 Comment(1)
great, i have apache CDN and playframework, i added letsencrypt for apache and then configure this part and working well with play2.5Equalizer
B
2

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
Bufordbug answered 30/6, 2020 at 14:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.