Java TLS 套接字 : No trusted certificate found

标签 java sockets ssl apple-push-notifications javaapns

让我快速解释一下我想要做什么。我正在尝试在 java 中构建我自己的 Apple 推送通知服务(用于测试目的)。这项服务的工作得益于 TLS 套接字。

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

但是,我在握手过程中遇到异常并且找不到解决方法。

注意:我在双方都使用相同的证书,它是一个标准的 .p12 文件,用于向 APNs 发送推送通知。

这是客户端(简化):

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);

这是一个异常(exception):
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found

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

服务器需要什么样的证书才能被客户端信任?

提前致谢。

最佳答案

编辑 : 我错了! tmf.init(null) 确实使用默认 keystore ,就像 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 中提取 key 和证书,
    替换或添加单个证书,并将它们重新组合在一起。
  • 为服务器创建不同的存储和 key ,并从已建立的 CA 获取证书(链)。
    通常要花一些钱,并且需要您证明对服务器域名的控制。
    (您的客户可以而且应该仍然使用这个 p12。两侧不必相同。)
  • 找到信任 anchor (来自 p12,或来自 CA 等其他地方)并拥有它
    在客户端显式加载的信任库中。您通过使用
    p12 作为信任库并说你不想要那个。
  • 将信任 anchor 放在客户端的默认信任库中,因此客户端继续
    使用默认值。如果您不介意修改您的 JRE(并且没有其他用户或应用程序
    在您的系统上被打扰)只需添加到 JRE/lib/security/cacerts。或者,假设您可以设置
    系统属性,将 anchor 放在商店中或将其留在 p12 中
    并将 javax.net.ssl.trustStore{,Password,Type} 设置为指向该商店。
    (如果你复制你应该只拿证书;p12 是一个 key 和证书,而不仅仅是一个证书。
    不要只是-importkeystore; -importcert 一个证书文件,必要时使用 -exportcert 创建。)
    (您可以在代码中使用 System.setProperty,但这会改变您的代码。如果您从
    命令行你可以使用'java -Dname = value ...'。对于其他情况 YMMV。)

  • 存在一种可能的“类型”问题:如果证书是使用 ExtendedKeyUsage 扩展名颁发的
    并且该值仅指定 TLSclient 而不是 TLSserver(CA 可以选择这样做)
    然后将它用于服务器可能无法正常工作——看起来 JSSE 强制执行 EKU 限制。
    但如果这是一个问题,你会得到一个非常不同的异常。
    您也可以在上面的 keytool -list -v 中看到这一点。

    由于您(正确地)希望将此 p12 用于您的客户端,因此您的服务器逻辑同样需要
    相信它。 (将其用于传出身份验证不会自动使其信任传入身份验证。)
    但仅当/当 clientAuth 实际完成时,这不是默认设置;你的服务器代码
    在接受连接之前 SSLServerSocket 上的 .setNeedClientAuth(true)?
    可能的方法与上述方法相同,只是跳过 #2 不适用。
    如果客户端和服务器都使用相同的 JRE,那么 cacerts 会更容易一些。

    最后,是的,TrustManager 'PKIX' 比'SunX509' 更新并且通常更具功能性。
    但是对于基本测试“是我们信任库中的信任 anchor ”,它们是等价的。

    再次为误导感到抱歉。

    关于Java TLS 套接字 : No trusted certificate found,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23129224/

    相关文章:

    java - JBoss MQ教程

    c# - 使用套接字获取对象处置异常

    wordpress - 如何在 AWS EC2 WordPress 站点上安装 SSL

    Java 客户端通过 SSL 使用 .NET WCF(不支持 WS-Policy)

    java - 变压器脱离 CR

    java - ERROR : Unable to start the daemon process. 此问题可能是由于守护程序配置不正确造成的

    eclipse中的JavaFX找不到外部jar

    objective-c - 无法在后台 iOS 中保持 Websocket 事件

    sockets - HaProxy(无法绑定(bind)套接字,选择测试失败)

    tomcat - 启用 SSL : Tomcat in Windows 7