How to configure a Play application to use Let'

2019-02-05 01:49发布

问题:

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?

回答1:

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



回答2:

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