如何从证书链、证书和私钥正确配置 HTTPS?阿卡HTTP
How to configure properly HTTPS from certificate chain, certificate and private key? Akka HTTP
简介
- 我必须使用 Akka HTTP 编写微服务程序。 [完成]
- 服务必须 运行 在 docker 容器内。 [完成]
- 与此服务的通信(通过 REST API [完成])已通过 HTTPS。 [待办事项]
问题
尝试从网络浏览器发出 HTTPS GET 请求时:
Browser warning connection not secure
尝试向服务器 上的服务发出 cURL 请求时:
docker ps
端口
0.0.0.0:443->443/tcp
curl -v https://localhost
- TCP_NODELAY 设置
- 200 毫秒后过期 4(传输 0x5648dd24df90)
- 连接到本地主机 (127.0.0.1) 端口 443 (#0)
- ALPN,提供 h2
- ALPN,提供 http/1.1
- 成功设置证书验证位置:
- CA文件:none
路径:/etc/ssl/certs
- TLSv1.3 (OUT),TLS 握手,客户端问候 (1):
- OpenSSL SSL_connect:SSL_ERROR_SYSCALL 与 localhost:443
有关
- 正在关闭连接 0
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL 连接到 localhost:443
问题
如何正确配置 Akka HTTP 服务器以使用 HTTPS,对于给定的:
- .pem-文件(包含根证书和中间证书),
- .cert 文件(包含我的域的证书),
- .key-文件(包含私钥) ???
到目前为止我做了什么?
- 我将 ca-certs 和 my-cert 连接到一个单独的 cert-chain.pem 文件中,并与私钥一起使用 openssl 创建了一个 p12 密钥库。
- 我写了下面的代码:
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
}
如果还不够,完整代码在我的publicrepository
我还做了什么
除了我阅读了很多关于 x509 标准和 TLS/HTTP 并在另一个线程中搜索答案的事实之外,我还检查了端口 443 在服务器上是否打开(顺便说一句:我在一个VPN 连接,所以防火墙应该无关紧要??)我向管理员求助,他不熟悉 Akka HTTP 和 docker,但他说 curl 输出可能与证书链有关。
您正在为此处的 TrustManager[]
参数传递 null
:
sslContext.init(X509KeyManagersFactory(p12KeyStore, keyStorePassword), null, new SecureRandom)
查看有关如何初始化和传递 TrustManager 的文档:
https://doc.akka.io/docs/akka-http/current/server-side/server-https-support.html#using-https
令人尴尬的是,问题与 Docker 和使用 openssl 创建的密钥库有关:容器内的根用户没有读取密钥库的权限(权限被拒绝异常)。
但是,该代码有效,我会保留此线程作为社区的参考。
确保端口 443 已打开并且 运行
telnet localhost 443
看看有没有像 scape character is '^]'.
确保您的服务器使用 https 协议而不是 http
curl --insecure -v https://localhost
跳过 SSL 证书验证(但仍建立 SSL 连接)以查看您是否真的收到来自服务器的响应
确保您的服务器证书是全局签名的而不是自签名的
您的浏览器不信任未知证书,请确保您从全球 CA 获得 SSL 证书,例如让我们加密。您的浏览器有一个硬编码 CA 证书列表,用于验证每个网站的证书。
另一方面,您的 curl 正在 /etc/ssl/certs
中查找以获取用于验证您网站证书的 CA 证书列表,因此标志 --insecure
是跳过验证 SSL 证书。
简介
- 我必须使用 Akka HTTP 编写微服务程序。 [完成]
- 服务必须 运行 在 docker 容器内。 [完成]
- 与此服务的通信(通过 REST API [完成])已通过 HTTPS。 [待办事项]
问题
尝试从网络浏览器发出 HTTPS GET 请求时:
Browser warning connection not secure
尝试向服务器 上的服务发出 cURL 请求时:
docker ps
端口
0.0.0.0:443->443/tcp
curl -v https://localhost
- TCP_NODELAY 设置
- 200 毫秒后过期 4(传输 0x5648dd24df90)
- 连接到本地主机 (127.0.0.1) 端口 443 (#0)
- ALPN,提供 h2
- ALPN,提供 http/1.1
- 成功设置证书验证位置:
- CA文件:none 路径:/etc/ssl/certs
- TLSv1.3 (OUT),TLS 握手,客户端问候 (1):
- OpenSSL SSL_connect:SSL_ERROR_SYSCALL 与 localhost:443 有关
- 正在关闭连接 0 curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL 连接到 localhost:443
问题
如何正确配置 Akka HTTP 服务器以使用 HTTPS,对于给定的:
- .pem-文件(包含根证书和中间证书),
- .cert 文件(包含我的域的证书),
- .key-文件(包含私钥) ???
到目前为止我做了什么?
- 我将 ca-certs 和 my-cert 连接到一个单独的 cert-chain.pem 文件中,并与私钥一起使用 openssl 创建了一个 p12 密钥库。
- 我写了下面的代码:
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
}
如果还不够,完整代码在我的publicrepository
我还做了什么
除了我阅读了很多关于 x509 标准和 TLS/HTTP 并在另一个线程中搜索答案的事实之外,我还检查了端口 443 在服务器上是否打开(顺便说一句:我在一个VPN 连接,所以防火墙应该无关紧要??)我向管理员求助,他不熟悉 Akka HTTP 和 docker,但他说 curl 输出可能与证书链有关。
您正在为此处的 TrustManager[]
参数传递 null
:
sslContext.init(X509KeyManagersFactory(p12KeyStore, keyStorePassword), null, new SecureRandom)
查看有关如何初始化和传递 TrustManager 的文档: https://doc.akka.io/docs/akka-http/current/server-side/server-https-support.html#using-https
令人尴尬的是,问题与 Docker 和使用 openssl 创建的密钥库有关:容器内的根用户没有读取密钥库的权限(权限被拒绝异常)。
但是,该代码有效,我会保留此线程作为社区的参考。
确保端口 443 已打开并且 运行
telnet localhost 443
看看有没有像 scape character is '^]'.
确保您的服务器使用 https 协议而不是 http
curl --insecure -v https://localhost
跳过 SSL 证书验证(但仍建立 SSL 连接)以查看您是否真的收到来自服务器的响应
确保您的服务器证书是全局签名的而不是自签名的
您的浏览器不信任未知证书,请确保您从全球 CA 获得 SSL 证书,例如让我们加密。您的浏览器有一个硬编码 CA 证书列表,用于验证每个网站的证书。
另一方面,您的 curl 正在 /etc/ssl/certs
中查找以获取用于验证您网站证书的 CA 证书列表,因此标志 --insecure
是跳过验证 SSL 证书。