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

时间:2012-08-14 19:09:36

标签: java ssl ssl-certificate

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

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

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

  

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

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

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

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

2 个答案:

答案 0 :(得分:4)

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

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

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

通常,您可以按如下方式初始化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感兴趣,因为密钥库和信任库之间存在差异。简而言之,两者都是存储意义上的“密钥库”,但密钥库是存储您的证书(+链)和相关私钥(即服务器上的服务器证书和私钥)的地方。客户端上的客户端证书和priv。密钥,信任库是您存储CA证书或远程证书的地方(没有私钥,因为它们不是您的证书),您愿意信任,当远程方出示证书。

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

编辑:(以下评论)

  

正确地理解我可以使用null   TrustManager如果我是服务器而尚未使用SSL客户端   认证

  

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

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

您还可以构建一个接受自签名证书(独立于任何CA)的TrustManager,并使用预定义列表中的指纹对其进行验证,但这会带来许多问题(特别是服务器要求的问题)正确的证书),你最终可能会复制PKI的部分内容。除非你更了解你正在做什么,否则这不是我推荐的。

  

这有点沮丧;我曾希望我能等到我有   我需要检索指纹之前的请求URL   授权客户端,然后才与实际的指纹进行比较   客户端使用。进行身份验证。

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

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

总的来说,重新协商与否,这完全独立于检查您如何信任客户端证书。如果您真的希望继续使用您的应用程序层(而不是SSL / TLS层)进行证书验证,那么您必须编写一个信任任何内容的X509TrustManager(绕过SSL的验证) / TLS层)并让您的应用程序从SSLSession(对等证书)获取证书并在那里进行验证。总的来说,这与接受自签名证书并手动验证它们非常相似。它可以完成,但是您将逐步退出PKI模型,并且您需要一些自定义代码来执行此操作(而不是仅使用API​​,因为默认情况下它将被使用)。

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

  

此外,<the key password>在这做什么?这应该运行   在某个托管设施的无人值守服务器上;我们不可以   等待有人在启动时输入密码   (比如停电后)。

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

答案 1 :(得分:0)

回答第1部分:

您需要为SSLContext提供KeyManager,因此服务器的SSLContext能够将其自身呈现给客户端,无论KeyManager存储为公钥和私钥。

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

编辑:回答第2部分:

所有这些类和接口使得可以为任何类型的事物使用不同的服务提供商(管理私钥,管理客户端身份验证,使用的SSL引擎等)。

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

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

编辑:回答第3部分:

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

如果要干扰ssl通信本身,则将使用SSLContext和SSLSession。在你甚至不使用Socket的地方。