没有 JKS 的嵌入式 Jetty 上的 TLS
TLS on Embedded Jetty without JKS
我的目标: 我正在致力于集成 Jetty Embedded,使其易于使用。 interface 将允许在不使用 Java KeyStore 的情况下集成 TLS 证书的外部源。
这将在构建分布式 Web 服务时提供更大的灵活性(在我的例子中是 experimental, self-hosted CDN)。
但是,我在构建集成时遇到了问题。存根实现是 in this repository.
我试过的方法: 我试过替换 key manager and the trust manager 并为其中的每个函数设置断点。但是,当尝试访问服务器时,这些断点永远不会被触发。相反,我遇到了这个错误:
javax.net.ssl.SSLHandshakeException: no cipher suites in common
at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1478)
at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:535)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:813)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:621)
at org.eclipse.jetty.server.HttpConnection.fillRequestBuffer(HttpConnection.java:322)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:231)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:112)
at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:261)
at org.eclipse.jetty.io.ssl.SslConnection.succeeded(SslConnection.java:150)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:112)
at org.eclipse.jetty.io.ChannelEndPoint.run(ChannelEndPoint.java:124)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:590)
at java.lang.Thread.run(Thread.java:748)
我已经尝试分析 "standard" Jetty 设置,它是来自密钥库的证书,但运气不佳。我找不到 Jetty 获取我应该覆盖的密码/证书信息的位置。
我的问题:如何让 Jetty 使用 my own certificate source 而不是 Java KeyStore 和 TrustStore?
@EJP 为我指明了正确的方向,下面是如何做到的:
这是需要完成的方法。
首先,为 TLS 设置 Jetty:
HttpConfiguration https = new HttpConfiguration();
https.addCustomizer(new SecureRequestCustomizer());
SslContextFactory sslContextFactory = new JettySslContextFactory(configuration.getSslProviders());
ServerConnector sslConnector = new ServerConnector(
server,
new SslConnectionFactory(sslContextFactory, "http/1.1"),
new HttpConnectionFactory(https)
);
sslConnector.setPort(httpsPort);
注意 class JettySslContextFactory。 class 扩展了内置的 X509ExtendedKeyManager 并且需要覆盖 protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception
方法以提供自定义 KeyManager,如下所示:
@Override
protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception {
return new KeyManager[] {
new JettyX509ExtendedKeyManager(certificateProviders)
};
}
除此之外,在每个连接中 运行 以下步骤:
- 使用 SNI 主机名查询 SNI 匹配器。这似乎是 SNI 主机名甚至可用的唯一地方。
- 咨询密钥管理器以获得特定密钥类型(EC 或 RSA)的别名(某种密钥 ID)。这里我们需要从 SNI 匹配器中获取主机名,否则我们将不知道要匹配哪个主机名。
- 根据别名(密钥 ID),我们可以 return 私钥和证书。
至少这是我从调试这个问题中收集到的。完整代码 is online here.
我的目标: 我正在致力于集成 Jetty Embedded,使其易于使用。 interface 将允许在不使用 Java KeyStore 的情况下集成 TLS 证书的外部源。
这将在构建分布式 Web 服务时提供更大的灵活性(在我的例子中是 experimental, self-hosted CDN)。
但是,我在构建集成时遇到了问题。存根实现是 in this repository.
我试过的方法: 我试过替换 key manager and the trust manager 并为其中的每个函数设置断点。但是,当尝试访问服务器时,这些断点永远不会被触发。相反,我遇到了这个错误:
javax.net.ssl.SSLHandshakeException: no cipher suites in common
at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1478)
at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:535)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:813)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:621)
at org.eclipse.jetty.server.HttpConnection.fillRequestBuffer(HttpConnection.java:322)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:231)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:112)
at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:261)
at org.eclipse.jetty.io.ssl.SslConnection.succeeded(SslConnection.java:150)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:112)
at org.eclipse.jetty.io.ChannelEndPoint.run(ChannelEndPoint.java:124)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:590)
at java.lang.Thread.run(Thread.java:748)
我已经尝试分析 "standard" Jetty 设置,它是来自密钥库的证书,但运气不佳。我找不到 Jetty 获取我应该覆盖的密码/证书信息的位置。
我的问题:如何让 Jetty 使用 my own certificate source 而不是 Java KeyStore 和 TrustStore?
@EJP 为我指明了正确的方向,下面是如何做到的:
这是需要完成的方法。
首先,为 TLS 设置 Jetty:
HttpConfiguration https = new HttpConfiguration();
https.addCustomizer(new SecureRequestCustomizer());
SslContextFactory sslContextFactory = new JettySslContextFactory(configuration.getSslProviders());
ServerConnector sslConnector = new ServerConnector(
server,
new SslConnectionFactory(sslContextFactory, "http/1.1"),
new HttpConnectionFactory(https)
);
sslConnector.setPort(httpsPort);
注意 class JettySslContextFactory。 class 扩展了内置的 X509ExtendedKeyManager 并且需要覆盖 protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception
方法以提供自定义 KeyManager,如下所示:
@Override
protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception {
return new KeyManager[] {
new JettyX509ExtendedKeyManager(certificateProviders)
};
}
除此之外,在每个连接中 运行 以下步骤:
- 使用 SNI 主机名查询 SNI 匹配器。这似乎是 SNI 主机名甚至可用的唯一地方。
- 咨询密钥管理器以获得特定密钥类型(EC 或 RSA)的别名(某种密钥 ID)。这里我们需要从 SNI 匹配器中获取主机名,否则我们将不知道要匹配哪个主机名。
- 根据别名(密钥 ID),我们可以 return 私钥和证书。
至少这是我从调试这个问题中收集到的。完整代码 is online here.