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

后端 未结 3 1713
离开以前
离开以前 2020-12-30 09:54

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

相关标签:
3条回答
  • 2020-12-30 10:16

    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

    0 讨论(0)
  • 2020-12-30 10:25

    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
    
    0 讨论(0)
  • 2020-12-30 10:28

    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

    0 讨论(0)
提交回复
热议问题