编辑 :我错了!tmf.init(null)就像sslctx.init(,null,)一样使用默认密钥库!该默认值通常是JRE / lib /
security中的cacerts文件,它确实信任许多已建立的CA,因此现在,我认为我们可以确信真正的服务器正在已建立的CA下使用一个证书(因此受您的客户端信任),而该证书在您的p12中显然没有;但是这里有两种可能性:
它是自签名的,或由未知,晦涩或未经验证的CA发出
它由需要一个(或多个)链证书的“中间” CA下的“真实” CA颁发,并且您的p12中没有链证书。请注意,这仍然可以对真实服务器进行客户端身份验证,因为真实服务器可以轻松地在其信任库中“预加载”链式证书,即使它们不在Java的信任库中也是如此。
为了区分这些,请查看
keytool -keystore file -storetype pkcs12 -list -v
并查看您在那里拥有的证书或证书序列。
然后可能有几种解决方法:
如果仅缺少已建立CA的链证书,则将其添加。keytool只允许您替换整个链,因此您必须获取所有需要的证书;openssl(如果有的话)可以从pkcs12中分解出密钥和证书,替换或添加单个证书,然后将它们重新结合在一起。
为服务器创建其他存储和密钥,并从已建立的CA获得证书(链)。通常会花费一些钱,并且需要您证明对服务器域名的控制。(您的客户可以并且应该仍然使用此p12。双方不必相同。)
找到信任锚(从p12或从类似CA的其他位置),并将其放置在客户端显式加载的信任库中。通过将p12用作信任库,您有效地尝试了此操作,并说您不想这样做。
将信任锚放置在客户端的默认信任库中,以便客户端继续使用默认值。如果您不介意修改JRE(并且系统上没有其他用户或应用程序被打扰),只需将其添加到JRE / lib / security / cacerts。或者,假设您可以设置系统属性,则将锚放置在商店中或将其留在p12中,然后将javax.net.ssl.trustStore {,Password,Type}设置为指向该商店。(如果您复制,则应该只获取证书; p12是密钥和证书,而不仅仅是证书。不要只是-importkeystore; -importcert是一个证书文件,必要时使用-exportcert创建。)(可以使用System.setProperty在您的代码中,但这正在更改您的代码。如果您从命令行运行,则可以使用’java -Dname = value …’。对于其他情况,则为YMMV。)
可能存在“类型”问题:如果证书是使用ExtendedKeyUsage扩展名颁发的,并且该值仅指定TLSclient而不指定TLSserver(CA可以选择这样做),则将其用于服务器
可能 无法正常工作-似乎是JSSE实施EKU限制。但是,如果这是一个问题,您将获得完全不同的Exception。您也可以在上方的keytool
-list -v中看到这一点。
由于您(正确地)想为客户端使用此p12,因此服务器逻辑同样需要信任它。(将其用于传出的身份验证不会自动使它成为传入的身份验证的受信任者。)但仅当/当clientAuth实际上完成时,这不是默认值;接受连接之前,您在SSLServerSocket上的服务器代码.setNeedClientAuth(true)是什么?可能的方法与上述方法等效,只是跳过#2不适用。如果客户端和服务器都使用相同的JRE,则使cacerts的方式更容易一些。
最后,是的,TrustManager’PKIX’比’SunX509’较新,并且通常功能更强大。但是对于基本测试“是我们信任库中的信任锚”,它们是等效的。
再次抱歉造成误导。



