¿Cómo configurar correctamente HTTPS de cadena de certificados, certificado y clave privada? Akka HTTP
Intro
- Tengo que programar un microservicio usando Akka HTTP. [Done]
- El servicio tiene que funcionar dentro de un contenedor de muelles. [Done]
- La comunicación (a través de REST API [Done]) con este servicio ha superado HTTPS. [TODO]
Problema
Mientras intenta hacer la solicitud HTTPS GET del navegador web:
Conexión de advertencia del navegador no segura
Mientras intenta hacer una solicitud de cURL al servicio en el servidor:
docker ps
PORTS
0,00,0,443-jó443/tcp
curl -v https://localhost
- TCP_NODELAY set
- Expire en 200 ms para 4 (transfer 0x5648dd24df90)
- Conectado al host local (127.0.0.1) puerto 443 (#0)
- ALPN, que ofrece h2
- ALPN, que ofrece http/1.1
- establecer con éxito los lugares de verificación de certificados:
- CAfile: ninguno CApath: /etc/ssl/certs
- TLSv1.3 (OUT), TLS Handhake, Cliente hola (1):
- OpenSSL SSL_connect: SSL_ERROR_SYSCALL en relación con localhost:443
- Conexión de cierre 0 curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL en relación con localhost:443
Pregunta
Cómo configurar correctamente el servidor de Akka HTTP para utilizar HTTPS, para un dado:
- .pem-File (contiene la raíz y certificados intermedios),
- .cert file (contiene el certificado para mi dominio),
- .key-File (contiene la llave privada) ??
¿Qué he hecho hasta ahora?
- Concateno las certidumbres y mi certidumbre a una sola cadena de cert. pem file y junto con la llave privada crearon una p12 key store con openssl.
- Escribí el siguiente código:
package de.htw_berlin.http
import java.security.SecureRandom
import akka.http.scaladsl.{ConnectionContext, HttpsConnectionContext}
import de.htw_berlin.security.{PKCS12KeyStoreFactory, X509KeyManagersFactory}
import java.io.FileInputStream
import javax.net.ssl.SSLContext
object HttpsConnectionContextFactory {
// TODO refactor
def apply(keyStoreFilename: String, keyStorePassword: String): HttpsConnectionContext = {
val p12KeyStore = PKCS12KeyStoreFactory(new FileInputStream(keyStoreFilename), keyStorePassword)
val sslContext: SSLContext = SSLContext.getInstance("TLS")
sslContext.init(X509KeyManagersFactory(p12KeyStore, keyStorePassword), null, new SecureRandom)
ConnectionContext.httpsServer(sslContext)
}
}
package de.htw_berlin.security
import java.security.KeyStore
import javax.net.ssl.{KeyManager, KeyManagerFactory}
object X509KeyManagersFactory {
def apply(keyStore: KeyStore, keyStorePassword: String): Array[KeyManager] = {
val keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
keyManagerFactory.init(keyStore, keyStorePassword.toCharArray)
keyManagerFactory.getKeyManagers
}
}
package de.htw_berlin.security
import java.io.InputStream
import java.security.KeyStore
object PKCS12KeyStoreFactory {
def apply(keyStoreAsInputStream: InputStream, keyStorePassword: String): KeyStore = {
val p12KeyStore = KeyStore.getInstance("PKCS12")
p12KeyStore.load(keyStoreAsInputStream, keyStorePassword.toCharArray)
p12KeyStore
}
}
package de.htw_berlin.http
import akka.actor.typed.ActorSystem
import akka.http.scaladsl.Http.ServerBinding
import akka.http.scaladsl.{Http, HttpsConnectionContext}
import de.htw_berlin.http.dip.RequestHandler
import scala.concurrent.Future
// TODO test.
/** Represents a HTTPS server which can be bind to a host and port.
* The server needs a request handler for processing incoming requests.
*
* @param host the optional host of the server. The default value is Constants.DefaultServerIpv4Address.
* @param port the optional port number of the server. The default value is Constants.DefaultHttpsPort.
* @param httpsContext the https connection context for the https connection.
* @param actorSystem an implicit actor system.
* @see de.htw_berlin.http.dip.RequestHandler
*/
class Server(val host: String = Constants.DefaultServerIpv4Address,
val port: Int = Constants.DefaultHttpsPort,
val httpsContext: HttpsConnectionContext)(implicit val actorSystem: ActorSystem[Nothing]) {
/** Binds the current server to the endpoint parameters (host and port) and uses the passed
* handler for processing incoming connections.
*
* @param handler the handler to be used for processing incoming requests.
* @return a server binding future.
*/
def bindAndHandleWith(handler: RequestHandler): Future[ServerBinding] =
Http().newServerAt(host, port).enableHttps(httpsContext).bind(handler.getRoute)
}
package de.htw_berlin.http
/** This object contains constants related with the current module http. */
object Constants {
/** The server's default IPv4 address. */
val DefaultServerIpv4Address = "0.0.0.0"
/** The default port for HTTPS connections. */
val DefaultHttpsPort = 443
}
Si no es suficiente, el código completo está en mi público. repositorio
¿Qué más he hecho?
Además del hecho de que leí mucho sobre x509 Standard y TLS/HTTP y busqué respuestas en otros hilos, también comprobé si el puerto 443 está abierto en el servidor (BTW: Hago todo dentro de una conexión VPN, así que el cortafuegos no debe importar?) y le pregunté al administrador para obtener ayuda, él no está familiarizado con Akka HTTP y docker, pero probablemente dijo que la cadena de salida
Pregunta hecha hace 3 años, 4 meses, 29 días - Por quantumquill
5 Respuestas:
-
Sorprendentemente, el problema estaba relacionado con Docker y la tienda clave creada con openssl: El usuario raíz dentro del contenedor no tenía el privilegio de leer la tienda clave (Excepción denegada por permiso).
Sin embargo, el código funciona y mantendré este hilo como referencia para la comunidad.
Respondida el Dec 16, 2020 a las 16:20 - por bytebard30f9
Votos positivos: 0 | Votos negativos: 0 -
Estás pasando
null
paraTrustManager[]
argumento aquí:sslContext.init(X509KeyManagersFactory(p12KeyStore, keyStorePassword), null, new SecureRandom)
Checkout la documentación sobre cómo inicializar y pasar TrustManager: https://doc.akka.io/docs/akka-http/current/server-side/server-https-support.html#using-https
Respondida el Dec 16, 2020 a las 16:28 - por codealchemy
Votos positivos: 0 | Votos negativos: 0 -
El puerto 443 está abierto y funcionando
telnet localhost 443
y ver si hay algo comoscape character is '^]'.
Asegúrese de que su servidor está hablando en protocolo https en lugar de https
curl --insecure -v https://localhost
para saltar la verificación del certificado SSL (pero aún establecer una conexión SSL) para ver si realmente recibe respuesta de su servidorAsegúrese de que su certificado de servidor está firmado a nivel mundial en lugar de auto-firmado
Su navegador no confía en certificado desconocido, por favor asegúrese de obtener un certificado SSL de CA global, por ejemplo. Encriptemos. Su navegador tiene una lista de certificados CA codificados para verificar el certificado de cada sitio web.
Por otro lado, tu rizo está mirando hacia arriba
/etc/ssl/certs
para obtener una lista de certificados de CA para verificar el certificado de su sitio web, así que la bandera--insecure
es evitar verificar el certificado SSL.Respondida el Dec 16, 2020 a las 16:35 - por bitcraftsman
Votos positivos: 0 | Votos negativos: 0 -
Para configurar correctamente un servidor Akka HTTP para utilizar HTTPS con un certificado y clave privada, necesitas seguir algunos pasos adicionales:
- Verificar tu certificado y clave privada: Asegúrate de que tu certificado y clave privada sean válidos y estén correctamente configurados.
- Crear un archivo de almacén de claves PKCS12: Utilizando openssl, puedes combinar tu certificado y clave privada en un archivo PKCS12. Por ejemplo:
bash openssl pkcs12 -export -out keystore.p12 -inkey privatekey.key -in certificate.crt
- Modificar tu código Akka HTTP: En tu código, puedes cargar el archivo de almacén de claves PKCS12 y configurar el contexto de conexión HTTPS. Puedes utilizar algo así:
scala import java.io.FileInputStream import javax.net.ssl.SSLContext import akka.http.scaladsl.{ConnectionContext, HttpsConnectionContext}
object HttpsConnectionContextFactory { def apply(keyStoreFilename: String, keyStorePassword: String): HttpsConnectionContext = { val keyStore = KeyStore.getInstance("PKCS12") keyStore.load(new FileInputStream(keyStoreFilename), keyStorePassword.toCharArray) val keyManagerFactory = KeyManagerFactory.getInstance("SunX509") keyManagerFactory.init(keyStore, keyStorePassword.toCharArray) val sslContext = SSLContext.getInstance("TLS") sslContext.init(keyManagerFactory.getKeyManagers, null, new SecureRandom) ConnectionContext.httpsServer(sslContext) } }
- Crear y usar el contexto de conexión HTTPS: En tu clase `Server`, puedes usar el contexto de conexión HTTPS que has creado en el paso anterior:
scala class Server(val host: String = Constants.DefaultServerIpv4Address, val port: Int = Constants.DefaultHttpsPort, val httpsContext: HttpsConnectionContext)(implicit val actorSystem: ActorSystem[Nothing]) { def bindAndHandleWith(handler: RequestHandler): Future[ServerBinding] = Http().newServerAt(host, port).enableHttps(httpsContext).bind(handler.getRoute) }
Con estos pasos, deberías poder configurar correctamente Akka HTTP para utilizar HTTPS con tu certificado y clave privada. Asegúrate de que el archivo de almacén de claves PKCS12 esté correctamente configurado y que tu código Akka HTTP esté cargando correctamente el contexto de conexión HTTPS.
Respondida el Dec 17, 2020 a las 16:23 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to localhost:443
significa que hay un problema con la configuración del certificado o con la clave privada.
Configuración correcta de HTTPS con una cadena de certificados, certificado y clave privada
Para configurar correctamente HTTPS con una cadena de certificados, un certificado y una clave privada en Akka HTTP, sigue estos pasos:
- Crea una cadena de certificados: Combina los certificados raíz e intermedios en un solo archivo `.pem`.
- Crea un archivo P12 almacén de claves: Convierte la cadena de certificados, el certificado y la clave privada en un archivo P12 almacén de claves utilizando la siguiente herramienta OpenSSL:
openssl pkcs12 -export -out keystore.p12 -inkey privateKey.key -in certificate.crt -certfile chain.pem
- Carga el almacén de claves P12: Utiliza el almacén de claves P12 para crear un contexto de conexión HTTPS:
scala val keyStore = KeyStore.getInstance("PKCS12") keyStore.load(new FileInputStream("keystore.p12"), "password".toCharArray) val sslContext = SSLContext.getInstance("TLS") sslContext.init(X509KeyManagersFactory(keyStore, "password"), null, new SecureRandom) val httpsConnectionContext = ConnectionContext.httpsServer(sslContext)
- Configura el servidor Akka HTTP: Utiliza el contexto de conexión HTTPS para configurar el servidor Akka HTTP:
scala Http().newServerAt("localhost", 443).enableHttps(httpsConnectionContext).bind(route)
Solución de problemas de conexión
Si sigues teniendo problemas para establecer una conexión segura, comprueba lo siguiente:
Asegúrate de que el almacén de claves P12 contiene la cadena de certificados, el certificado y la clave privada correctos. Verifica que la contraseña proporcionada para el almacén de claves es correcta. Comprueba si el puerto 443 está abierto en el servidor y no está bloqueado por un firewall. Intenta utilizar una herramienta como
openssl s_client
para conectarse al servidor HTTPS y verificar si el certificado es válido y confiable.Ejemplo de código completo
scala import akka.actor.{ActorSystem, Props} import akka.http.scaladsl.Http import de.htw_berlin.http.dip.RequestHandler import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory} import java.io.FileInputStream import java.security.{KeyStore, SecureRandom} object Main extends App { implicit val actorSystem: ActorSystem = ActorSystem() val keyStore = KeyStore.getInstance("PKCS12") keyStore.load(new FileInputStream("keystore.p12"), "password".toCharArray) val keyManagerFactory = KeyManagerFactory.getInstance("SunX509") keyManagerFactory.init(keyStore, "password".toCharArray) val trustManagerFactory = TrustManagerFactory.getInstance("SunX509") trustManagerFactory.init(keyStore) val sslContext = SSLContext.getInstance("TLS") sslContext.init(keyManagerFactory.getKeyManagers, trustManagerFactory.getTrustManagers, new SecureRandom) val httpsConnectionContext = ConnectionContext.httpsServer(sslContext) val requestHandler = actorSystem.actorOf(Props(new RequestHandler())) Http().newServerAt("localhost", 443).enableHttps(httpsConnectionContext).bind(requestHandler.getRoute) }
Espero que esto te ayude a resolver el problema y configurar correctamente HTTPS en tu microservicio Akka HTTP.
Respondida el Dec 17, 2020 a las 16:28 - por Gemini
Votos positivos: 0 | Votos negativos: 0