当前位置: 首页 > 面试题库 >

Java TLS套接字:找不到可信证书

聂琨
2023-03-14
问题内容

让我快速解释一下我要做什么。我正在尝试用Java构建自己的Apple的Push
Notification服务(出于测试目的)。此服务的工作归功于TLS套接字。

我有一个Java客户端来创建TLS套接字,以将推送通知发送到APN。我更改了主机URL,以将套接字重定向到localhost:2195。现在,我试图编写一个Java套接字服务器来获取通知请求。

但是,握手过程中出现异常,找不到解决方法。

注意:我在两面都使用相同的证书,这是一个标准的.p12文件,可用于向APN发送推送通知。

这是客户端(简体):

KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(certificatePath), password.toCharArray());

KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
kmf.init(ks, password.toCharArray());

TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509"); 
tmf.init((KeyStore)null);

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

SSLSocketFactory ssf = sc.getSocketFactory(); 
SSLSocket socket = (SSLSocket) ssf.createSocket(InetAddress.getLocalHost(), 2195);
socket.startHandshake();

这是服务器:

KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(certificatePath), password.toCharArray());

KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
kmf.init(ks, password.toCharArray());

SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), null, null);

SSLServerSocketFactory ssf = context.getServerSocketFactory();
serverSocket = (SSLServerSocket) ssf.createServerSocket(2195);

这是一个例外:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found

我想客户端不信任服务器的证书。我尝试将客户端的TrustManager设置为接受服务器的p12,并且它可以正常工作,但是我需要在不编辑客户端的情况下也可以正常工作(因为它可以与实际的APN一起工作)。

服务器需要哪种证书才能被客户端信任?

提前致谢。


问题答案:

编辑 :我错了!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
并查看您在那里拥有的证书或证书序列。

然后可能有几种解决方法:

  1. 如果仅缺少已建立CA的链证书,则将其添加。keytool只允许您替换整个链,因此您必须获取所有需要的证书;openssl(如果有的话)可以从pkcs12中分解出密钥和证书,替换或添加单个证书,然后将它们重新结合在一起。

  2. 为服务器创建其他存储和密钥,并从已建立的CA获得证书(链)。通常会花费一些钱,并且需要您证明对服务器域名的控制。(您的客户可以并且应该仍然使用此p12。双方不必相同。)

  3. 找到信任锚(从p12或从类似CA的其他位置),并将其放置在客户端显式加载的信任库中。通过将p12用作信任库,您有效地尝试了此操作,并说您不想这样做。

  4. 将信任锚放置在客户端的默认信任库中,以便客户端继续使用默认值。如果您不介意修改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’较新,并且通常功能更强大。但是对于基本测试“是我们信任库中的信任锚”,它们是等效的。

再次抱歉造成误导。



 类似资料:
  • 我想找个人来澄清一个我相信我有的误解。我在读Java上的套接字编程,并不真正理解事情的实际流程是什么。以下是我对以下场景的两种可能的解释。 创建套接字实例 null 服务器接受客户端之后的连接会是这样的吗?(客户端与服务器创建的套接字通信,而不是与服务器套接字通信) 或者图表会像这样吗?(客户端通过ServerSocket与服务器保持通信。服务器通过接受连接时创建的套接字进行通信。)

  • 我有以下代码要(尝试)写入服务器: 我在远程计算机上的服务器代码中有一个断点,我有一个通过这个套接字通信的Visual Basic程序。我运行了Visual Basic代码,并(反复)验证服务器是否在那里,监听这个套接字,并且通过运行该程序可以触及断点。 当我运行这个程序时,我不会碰到断点。我尝试了设置套接字的各种方法,例如只使用“new socket()”实例化套接字,然后使用setter方法设

  • 问题内容: android应用有三台主机进行身份验证和授权。最终主机是REST API。首次使用Oauth身份验证和授权过程,它可以正常工作。 但是,如果 用户 在登录并访问REST API提供的服务后 杀死了该应用程序 ,然后再次打开该应用程序,则会出现此问题。 在这段时间内,身份验证和授权过程不会发生,只有REST API会发生。 这是造成原因, 但在首次使用(登录然后使用该应用程序)期间正在

  • 我正在Android上开发一个客户端应用程序,它连接到我的服务器,它在负载均衡器后面的AWS上,我在GoDaddy上创建了一个SSL证书,并将其添加到负载均衡器上。 浏览器上的一切都很顺利,它可以识别证书,但是当我试图用Android调用API时,我遇到了一个例外: 我找到了一些讨论谁说在app上添加证书,但是在证书服务器端是不是就没有办法修复了呢?这不是证书的问题吗?

  • 问题内容: 我正在创建一个Go TCP服务器(不是http / s),并且试图将其配置为使用SSL。我有一个StartCom免费SSL证书,正试图用来完成此任务。我的服务器代码如下: 我尝试切换证书的顺序,但不包括某些证书等,但是的输出基本上保持不变。有关完整输出,请参见此处。我对中间证书似乎做错了,但是我不知道该怎么办。我已经为此工作了几天,进行了大量的谷歌搜索和搜索,但似乎没有什么适合我的情况

  • 我遇到了Android4设备的一个问题,它在连接到服务器时收到以下异常: 但出于某种原因,我不明白它在Android4版本中开始失败了。 我尝试了信任所有证书的解决方案[LINK],它起作用了,但这显然存在安全问题,比如将您的应用程序暴露于“中间人”攻击 如何实现具有默认行为但仅将服务器的证书白名单的TrustManager。 在证书链给了我两个证书的格式: 用获得的url和证书替换了示例中的ur