【问题标题】:Conceptual overview of server-side SSL in Java [closed]Java中服务器端SSL的概念概述[关闭]
【发布时间】:2012-08-14 19:09:35
【问题描述】:

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

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

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

我应该开始阅读哪些文档以了解如何做到这一点?

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

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

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

【问题讨论】:

  • SSLContext 文档表明所有指定给 init() 的参数都可以为空。您是否尝试过将其传递为空值并查看其行为方式?
  • 你的网络服务是 JEE 战争吗?
  • 好的,一件一件的。 SSLContext 是 SSLContextSPI 的包装器,例如它包含常用方法,以使用服务提供者。 KeyManager 为上下文提供它自己的私钥。 TrustManager 为上下文提供它可以信任的公钥(例如,您可以使用它来进行客户端身份验证)
  • @Wug:是的,我可以传递 null。但我肯定必须以某种方式告诉 JRE 在哪里可以找到它应该授权给客户端的证书。
  • @kw4nta:如果SSLContext 包装了SSLContextSPI,那么它会私下进行。没有公共方法可以用来解决后者,即使我知道如何处理它。

标签: java ssl ssl-certificate


【解决方案1】:

如果您想要详细的文档,请查看JSSE Reference Guide,更具体地说是SSLContext section

默认值(如果您将null 传递给SSLContext.init(...))默认是合理的,但您可能想了解这些默认值是什么(请参阅Customization 部分)。

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

通常,您可以按如下方式初始化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);

也许您可能还对this answer 感兴趣,以了解密钥库和信任库之间的区别。简而言之,两者都是存储意义上的“密钥库”,但密钥库是您存储您的证书(+链)和相关私钥(即服务器证书和服务器上的私钥)和客户端证书和私钥)和信任库是您存储 CA 证书或远程证书(没有私钥,因为它们不是您的证书)的地方,当远程方出示证书。

关于如何处理您的密钥/证书文件,最简单的方法当然是使用PCKS12 类型的密钥库,您可以按照this answer 中的说明构建它。

编辑:(跟随 cmets)

所以是否正确理解我可以逃脱 null TrustManager 如果我是服务器但尚未做 SSL 客户端 认证?

是的。

但是,如果我要进行客户身份验证,我需要提供一个 包含每个客户端的公钥的 TrustManager 可以连接吗?

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

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

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

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

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

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

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

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

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

【讨论】:

  • 谢谢!那么,如果我是服务器并且尚未进行 SSL 客户端身份验证,是否可以正确理解我可以使用 null TrustManager 侥幸?但是,如果我要进行客户身份验证,我需要提供一个 TrustManager,其中包含应该能够连接的每个客户端的公钥?这有点令人沮丧。我曾希望我可以等到获得请求 URL,然后才需要为授权客户端检索指纹,然后再与 actual 客户端进行身份验证的指纹进行比较。
  • 另外,&lt;the key password&gt; 在这里做什么?这应该在某处托管设施的无人值守服务器上运行;我们不能等待有人在启动过程中输入密码(例如,停电后)。
  • 客户端身份验证问题可能是一个单独的问题,但是:我想到的是,我们现有的用于让客户更改其 密码 的 API/基础设施可能是改编成一个他们设置“我想授予访问我的帐户的公钥的指纹”而不是密码的地方。然后,当我获得 URL 并找出他们想要访问的帐户时,我可以检查他们已经用来握手的证书是否具有为该帐户授权的指纹。这样,用户就不必(?)为成为我们客户的特权向 CA 付费。
  • 您不能真正将原始公钥直接用于 SSL/TLS(与 SSH 不同),它们必须在证书中,可能是自签名的(尤其是因为 JSSE 仅支持 X.509证书,就像很多 SSL 堆栈一样)。用户必须生成自己的证书,这对大多数用户来说可能有点棘手。如果您想免费为用户提供证书,您可以设置一个免费的 CA,让他们在浏览器中生成他们的密钥,并从您的服务器颁发证书。
  • 但是切换到 SSL 客户端身份验证而不是 RFC-2617-over-SSL 的全部意义在于,客户永远不需要信任我们可以用来冒充他的数据。如果是我们生成密钥对(并将它们包装在证书中),那么这个练习就变得毫无意义了。我假设证书内的某个地方仍然有一个可以检查和指纹识别的公钥。 (这比我构建的基于 GPG/PGP 的系统要复杂得多,如果它对我们的目标市场来说足够企业化的话)。
【解决方案2】:

回答第 1 部分:

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

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

编辑:回答第 2 部分:

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

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

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

编辑:回答第 3 部分:

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

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

【讨论】:

  • 我问的是我应该读什么以获得正确的理解,而不是一连串无关的事实,每个都引发进一步的问题 - 例如:我如何得到从我的一堆 PEM 文件到 KeyManager?如何获得 TrustManager?各自的目的是什么?我需要了解高级别的东西,因为我不会部署我完全不了解的“安全”解决方案。
  • 我简要概述了这个低级 API 提供了什么。我试图沟通,为了您的目的,您将使用 SSLSocket 和 SSLSocketFactory API(这反过来可能会或可能不会使用您想了解的 API)。然后根据您选择的 SSLSocketFactory 实现,您可以在其中配置信任和密钥等所有内容。所以我个人会从那一点开始。
  • 我很确定我们需要 SSLEngine 而不是 SSLSocket,因为我们现有的实现到处都使用非阻塞 I/O。而且您实际上并没有指出任何文档,即使我已经解释了两次(现在三次)我需要高级理解,而不是魔术般的做这个和这个代码什么我不知道它有什么作用或为什么它是必要的。
  • 您所说的“合理安全”到底是什么意思?我们是否可以推断您知道 Sun 实施中的已知安全漏洞?除了你的第 1 部分之外,这里没有任何东西可以真正回答这个问题。
猜你喜欢
  • 2014-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-15
  • 2022-01-20
相关资源
最近更新 更多