5

我的任务是使用 HTTPS 保护(以前是 HTTP)Web 服务。SSLEngine我从一位现已离职的同事那里继承了在我们现有服务器的 TCP 和 HTTP 层之间插入对象的代码。据我所知,这段代码可以正常工作。我得到了SSLEnginefrom SSLContext.createSSLEngine(),但是如何产生适当的SSLContext让我感到困惑。

SSLEngine它本身在其 javadoc 中有一个漂亮的概念介绍,但不幸的是,我不需要与自己进行交互。另一方面,SSLContext.init()的文档很少,只是说我必须通过“身份验证密钥的来源”和“对等身份验证信任决策的来源”,我不知道那是什么。这些参数类型的文档(通常我下次尝试理解它)是通用的,以至于什么都不说,而且类文档SSLContext也很简短。

我得到了一堆 ascii-armored .crt.pem.key文件,它们一起使 Apache 能够在 Java 服务器最终将直接处理的域中提供 HTTPS。我想我需要以某种方式将它们加载到 theSSLContext或 the 中SSLEngine,但不确定是否SSLContext.init()是正确的地方(尽管似乎没有很多其他地方可以)。

我应该首先阅读哪些文档以了解如何执行此操作?

我的 Google 尝试生成了许多质量和安全性未知的半未记录示例代码,以及一些高级演练,例如“如何编写自己的密钥提供程序”,但没有对 JRE的最基本用法进行全面的概念介绍类。

特别是因为这是与安全相关的,所以我没有用复制粘贴示例代码,我只会漫无目的地敲打,直到它似乎或多或少地做我想要的。我需要对各个部分实际上应该如何组合在一起的高级概念理解。

(如果文档足够详细,可以让我弄清楚如何在实践中进行 SSL 客户端授权,则可以加分——但这并不是立即紧迫的)。

4

2 回答 2

4

如果您想要详细的文档,请查看JSSE 参考指南,更具体地说是其SSLContext 部分

默认值(如果您传递nullSSLContext.init(...))默认情况下是合理的,但您可能想了解这些默认值是什么(请参阅自定义部分)。

密钥库没有默认值(只有信任库,如果您想要客户端证书身份验证,您几乎肯定会想要自定义它)。

SSLContext通常,您可以按如下方式初始化 a :

KeyStore ks = KeyStore.getInstance(...); // Load the keystore
ks.load(...); // Load as required from the inputstream of your choice, for example.

KeyStore ts = KeyStore.getInstance(...); // Load the truststore
ts.load(...);

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, <the key password>);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

对于密钥库和信任库之间的区别,您可能也对这个答案感兴趣。简而言之,两者都是存储意义上的“密钥库”,但密钥库是您存储证书(+链)和相关私钥(即服务器上的服务器证书和私钥以及客户端证书和私钥)的地方在客户端上),并且信任库是您在远程方提供其证书时存储您愿意信任的 CA 证书或远程证书(没有私钥,因为它们不是您的证书)的地方。

关于如何处理您的密钥/证书文件,最简单的方法当然是使用 type 的密钥库,您可以按照此答案PCKS12中的描述构建它。

编辑:(以下评论)

那么,如果我是服务器并且尚未进行 SSL 客户端身份验证,是否可以正确理解我可以使用 null TrustManager 侥幸?

是的。

但是,如果我要进行客户身份验证,我需要提供一个 TrustManager,其中包含应该能够连接的每个客户端的公钥?

通常不会。您将为颁发这些客户端证书的 CA 提供 CA 证书。如果您没有此基础架构(或者如果您正在构建它并且还没有任何客户端证书),您应该考虑创建自己的 PKI,或者可能从知名 CA 购买客户端证书。

您还可以构建一个 TrustManager 接受自签名证书(独立于任何 CA)并使用预定义列表中的指纹验证它们,但这会带来许多问题(特别是服务器如何要求正确的证书),您最终可能会复制 PKI 的部分用途。除非您对自己的工作有更多了解,否则我不建议这样做。

这有点令人沮丧。我曾希望我可以等到获得请求 URL,然后才需要为授权客户端检索指纹,然后才能与实际客户端进行身份验证的指纹进行比较。

在这里,您谈论的是完全不同的方面。如果您想先获取请求的 URL,在请求证书之前,您需要使用重新协商。HTTP 层必须与SSLEnginefor 对话,请求它触发新的握手(现在设置为请求客户端证书)。

SSLEngine一般来说,在 Java 中通向 SSL/TLS 并不容易,但异步重新协商可能会变得非常棘手。事实上,它的语义在应用层还不是很清楚。您很可能在收到 HTTP 请求后触发重新协商,但同时正在发送响应(因为您可能有异步请求/响应,可能是流水线的)。从技术上讲,新的握手会影响两个管道。

总体而言,无论是否重新协商,这与检查您如何信任客户端证书完全无关。如果你真的想让你的应用程序层(而不是 SSL/TLS 层)进行证书验证,你必须编写一个X509TrustManager信任任何东西(绕过 SSL/TLS 层的验证)和让您的应用程序从SSLSession(对等证书)获取证书并在那里进行验证。总的来说,这与接受自签名证书并更手动地验证它们非常相似。可以这样做,但是您将退出 PKI 模型,并且您需要一些自定义代码来执行此操作(而不是仅使用默认使用的 API)。

如果你对这一切都不熟悉,我会避免这种方法。尝试设置一个测试 CA 并首先了解它是如何工作的。关于使用 CA 或进行手动指纹验证的整个问题最终更多的是一个管理问题:它由您将如何在相关各方之间分发证书来定义。

另外,<the key password>这里在做什么?这应该在某处托管设施的无人值守服务器上运行;我们不能等待有人在启动过程中输入密码(例如,停电后)。

您需要设置密码。如果需要,大多数服务器会从配置文件中读取它(或者更糟糕的是,您可以对其进行硬编码)。

于 2012-08-14T19:39:22.483 回答
0

回答第 1 部分:

您需要为 SSLContext 提供一个 KeyManager,以便服务器端的 SSLContext 能够将自己作为 KeyManager 存储为公钥和私钥的任何内容呈现给客户端。

您需要为 SSLContext 提供一个 TrustManager 以允许它对客户端进行身份验证。

编辑:回答第 2 部分:

所有这些类和接口都可以将不同的服务提供者用于任何类型的事情(管理私钥、管理客户端身份验证、使用的 SSL 引擎等)。

但是 sun 提供了它自己的默认实现,这些实现相当安全。

所以:如果你想使用自定义的东西或者不是默认的东西,你通常只会深入到 api 中。

编辑:回答第 3 部分:

通常你会使用 SSLSocket 并放置一个普通的 Socket(或创建 SSLSocket,所以它也会创建普通的 Socket)。

如果您想干预 ssl 通信本身,您将使用 SSLContext 和 SSLSession。在你甚至不会使用 Socket 的地方。

于 2012-08-14T19:23:46.277 回答