java - 管理Java(自定义)信任库中的多个条目

标签 java security ssl keystore truststore

我已经阅读了一些相关的问题,但没有一个对我有真正的帮助。所以,让我给你一个背景故事:
我正在编写一个投票系统,其中有几个用于管理注册,交换投票卡和投票的服务器。在注册用户提交个人数据期间,服务器将检查数据是否与数据库中的数据匹配,然后接受用户的证书并将其添加到其trustStore中,如下所示:

KeyStore.Entry entry = new KeyStore.TrustedCertificateEntry(cert);
trustKeyStore.setEntry(alias, entry, null);


然后将trustStore存储在文件中。在注册过程中,许多用户注册了他们的证书,然后他们连接到另一台同时请求客户端和服务器身份验证的服务器,从而出现了问题。我以为该“其他服务器”可以使用前面提到的trustStore与用户执行SSLHandshake,但是似乎trustStore仅“记住”注册的最后一个用户。我正在创建SSLServerSocket像这样:

    tmf.init(trustKeyStore);
    kmf.init(keyStore, password);

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

    // create SSLSocketFactory and SSLSocket
    SSLServerSocketFactory sslSocketFactory =   (SSLServerSocketFactory)sslCtx.getServerSocketFactory();
    sslSocket = (SSLServerSocket)sslSocketFactory.createServerSocket(1234);


如前所述,双向身份验证工作正常,但仅适用于trustStore中的最后一个条目。所以,现在最后我的问题是-我是否必须为每个用户创建一个单独的trustStore?我是否需要重载TrustManager?基本上,有什么方法可以使SSLContext / SSLEngine遍历所有受信任的证书,并在找到匹配的证书时进行握手?

更新

我认为我需要澄清一些事情。首先,这不是Web应用程序,只是普通的客户端服务器Java swing应用程序。使用的每个证书(服务器或客户端)都是自签名的,但是服务器的证书内置在客户端应用程序中,因此,当客户端连接到服务器时,他可以比较证书(有效)。我还尝试按照Bruno的建议完全解决问题-在用户注册后,服务器检查其数据是否有效,是否为客户端颁发新证书,将其添加到他的信任库中并将其发送给客户端。但是,即使对于最后一个注册的客户,这也不起作用。

最佳答案

(在更改问题后编辑。)

方法1:

解决此问题的一种方法是使用CA,可能是您自己的CA。

方法2:

在握手级别接受任何客户端证书。尽管从客户端的角度接受任何服务器证书都是一个坏主意,但是由于它引入了MITM攻击的可能性(例如,请参见herehere),因此在握手期间不接受任何客户端证书同样的问题。

在握手结束时,只要服务器证书仍然得到验证,您就会知道:


就像没有客户端证书身份验证的HTTPS一样,安全地建立了客户端和服务器之间的通信。
客户端具有其在握手期间提供的证书的私钥。 TLS CertificateVerify消息可以保证这一点,该消息使用客户端的私钥来签名到目前为止在握手期间交换的所有消息(包括服务器证书和客户端证书)的串联。这实际上不依赖于客户端证书本身的(信任)验证,并且仅在客户端证书中的公钥可以验证TLS CertificateVerify消息中的签名时才起作用。


这样,您将知道服务器获得的客户端证书由拥有匹配私钥的人使用。
您不会知道该公钥证书属于谁,即另一端的用户是谁,因为您缺少通常使用CA进行的验证步骤,而该CA本身应该依赖于CA颁发证书之前的波段机制。由于他们使用的是自签名证书,因此无论如何,您可能都必须执行此带外步骤,可能需要在注册过程中通过一些信息来执行。

这样做可以让您更轻松地管理用户及其使用证书的方式。您可以简单地在服务器公用的数据库中存储和/或查找当前用户的证书。您可以通过查看应用程序中的SSLSession来访问证书。您将在getPeerCertificates()返回的数组的位置0中找到用户证书。然后,您可以将您看到的凭据(再次,因为双向握手成功)与之前看到的凭据进行比较,以实现应用程序中的身份验证(和授权)。

请注意,即使您只是接受不断添加的自签名证书,除主题DN之外,您仍然必须跟踪此公钥信息作为系统的一部分,因为您无法控制主题DN(两个用户可以选择是否有意使用相同的内容)。

要实现此目的,您需要使用SSLContext配置服务器,该服务器使用X509TrustManager且在checkClientTrusted方法中不会引发任何异常。 (如果您将相同的SSLContext用于其他目的,我将获得默认的PKIX TrustManager并将委派给checkServerTrusted的调用委派给它。)由于您可能不知道主题/发行者是什么这些自签名证书的DN将是,您应该通过在getAcceptedIssuers()中返回一个空数组来发送一个已接受CA(*)的空列表(请参见示例here)。



(*)TLS 1.0在此主题上保持沉默,但是TLS 1.1明确允许使用空列表(具有未指定的行为)。实际上,它适用于大多数浏览器,甚至适用于SSLv3 / TLSv1.0:在这种情况下,浏览器将提供完整的证书列表供您选择(或选择它发现的第一个证书,并将其配置为自动选择一个)。



(我将在这里保留关于HTTP的更详细的内容。。。现在有点脱离上下文了,但这可能会让其他人感兴趣。)

方法1:

您可以将证书发行作为初始注册的一部分。当用户注册其详细信息时,他们还将申请证书,该证书将由您的注册服务器立即发布到其浏览器中。 (如果您不熟悉此方法,则可以使用IE上的<keygen />CRMF(在Firefox上)或ActiveX控件(取决于Windows版本)在浏览器中颁发证书。)

方法2:

在Servlet环境中,可以在servlet request javax.servlet.request.X509Certificate属性中获取它。

另外,由于拒绝客户端证书并不一定会终止连接,因此这往往会改善用户体验。您仍然可以提供一个网页(也许从技术上讲,它的状态为HTTP 401,但从技术上讲,它需要伴随一种质询机制,而对于证书而言则没有这种机制),至少可以告诉用户出了什么问题。握手失败(如果发生问题,握手中的客户端证书验证会导致握手失败)可能会使真正不了解证书的用户感到困惑。缺点是,由于大多数浏览器都缺乏用户界面支持,因此很难“注销”(类似于HTTP Basic身份验证)。

对于实现,如果您使用的是Apache Tomcat,则可能对this SSLImplementationthis answer感兴趣,如果您使用的是Apache Tomcat 6.0.33或更高版本。

关于java - 管理Java(自定义)信任库中的多个条目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9089056/

相关文章:

java - Lombok:@RequiredArgsConstructor 从final 创建构造函数,但不是从@NotNull 创建构造函数

php - 在多开发 nginx 设置中保护密码

c# - 为 ServicePointManager.SecurityProtocol 设置每个请求值

security - 如何验证 JavaScript 库?

php - 在 PHP 中加密 cookie

.htaccess - 如何重定向到根目录但使用 https

java - Android < 5.0 (Lollipop) 中存在 TLSv1.2 问题的套接字握手

java - 如何在 Swing 应用程序中运行 Javafx 舞台/场景?

java - 概括java中的4种方法

java - 如何更改 java.net.http.HttpClient 的用户代理字符串