首先,简要介绍一下公钥密码学中的术语:
- 您使用私钥签名和解密/解密,
- 您使用公钥验证(签名)和加密/加密。
(您并没有真正使用公钥“解密”。)
使用带有或不带有客户端身份验证的 SSL/TLS,服务器会提供一个证书 (*),它拥有它的私钥。服务器在 SSL/TLS 握手期间(连接开始时)发送其证书,并且能够使用其私钥(它保持私有)解密客户端发送的内容。私钥和证书存储在服务器的 keystore 中(如果未在 Java 中实现,则为等价物)。
作为其中的一部分,客户端使用其 truststore(一种包含受信任证书的密钥库)来验证服务器证书。服务器证书可以通过在信任库中显式获得信任,或者在大多数情况下,通过链接到信任库 (PKI) 中的受信任 CA 证书来信任。
Java 中 keystore 和 truststore 之间的术语可能有点混乱,您可以找到更多 details in this answer。
关于您的问题,客户端的信任库不包含服务器的公钥,而是它的证书或 CA 证书,它应该可以用来验证。 (这不仅仅是关于拥有公钥,而是知道它是谁,使用证书中的其他信息。)
除此之外,当您使用客户端证书身份验证时,服务器端有一个信任库(或等效),客户端也有一个密钥库,因为为此目的角色颠倒了。
在使用客户端身份验证的 SSL/TLS 握手中,服务器向客户端请求证书,客户端发送证书(如果可用)。
在此握手结束时,客户端发送CertificateVerify 消息,该消息使用客户端证书私钥对迄今为止在客户端和服务器之间交换的所有消息(因此双方都知道)进行签名。然后,服务器能够根据作为此交换的一部分获得的客户端证书中的公钥验证此签名。这向服务器证明,无论在客户端的任何人都拥有与它发送的证书中的公钥对应的私钥。
服务器的下一步是验证是否信任此证书,即是否信任证书中呈现和“密封”的身份与公钥之间的绑定。
这通常使用 PKI 来完成,您可以根据已知 CA 来检查证书,或者如果您的部署环境足够小,则可以根据一组固定的受信任证书来检查证书。 (可能有alternative methods of verification,但它们的可用性实际上取决于您要部署此系统的环境。)
因此,对于你的第二个问题:
- 客户端keystore至少应包含客户端的证书及其私钥。
- 客户端 truststore 应包含服务器证书或可以验证服务器证书的 CA 证书。
由于 keystore 和 truststore 都是用于不同目的的 keystore(在存储格式意义上,通常是文件),因此通常可以使用同一个 keystore 来同时满足 keystore 和 truststore 的目的。
(*) 有一些密码套件不依赖于证书,但这是不寻常的,并且与此问题无关。