Java中服务器端SSL的概念性概述

Hen*_*olm 5 java ssl ssl-certificate

我的任务是使用HTTPS保护(以前的HTTP)Web服务.从现在离开的同事我继承了SSLEngine在现有服务器中的TCP和HTTP层之间插入对象的代码.据我所知,这段代码可以正常工作.我得到了SSLEngine来自SSLContext.createSSLEngine(),但如何产生一个合适的SSLContext混淆我.

SSLEngine它本身在javadoc中有一个很好的概念性介绍,但遗憾的是我不需要与自己接口.另一方面,SSLContext.init()是非常稀疏的文档,只是说我必须传递"身份验证密钥的来源"和"对等身份验证信任决策的来源",我不知道那是什么.这些参数类型的文档(通常是我下一次尝试理解它)是通用的,没有说什么,而且类文档SSLContext也是无用的简短.

我提供了一串ASCII装甲.crt,.pem.key文件,它们一起使Apache将HTTPS服务在域Java服务器最终将直接处理.我想我需要将它们加载到SSLContext或以SSLEngine某种方式,但我不确定是否SSLContext.init()甚至是正确的地方(虽然它似乎没有其他许多地方).

我应该哪些文档开始阅读,以便了解如何执行此操作?

我的谷歌试图生成大量未经证实的半无证的示例代码,质量和安全性未知,以及一些高级演练,如"如何编写自己的密钥提供程序",但没有对JRE 最基本用法的整体概念介绍类.

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

(如果文档的详细程度足以让我弄清楚如何在实践中进行SSL客户端授权,那么奖励点 - 但这并不是立即紧急的).

Bru*_*uno 5

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

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

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

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

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);
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:(以下评论)

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

是的。

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

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

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

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

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

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

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

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

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

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