无法连接到 ssl 服务器收到致命警报:certificate_unknown 和 ReadDataRecord(SSLSocketImpl

unable to connect to ssl server Received fatal alert: certificate_unknown and ReadDataRecord(SSLSocketImpl

我的情况是客户端 A 应该连接到服务器 B,服务器 B 应该连接到服务器 C.and 连接必须是安全套接字。

我尝试并成功建立了连接 b/w A 到 B 和 B 到 C individually.But 当我尝试同时建立到 A 到 B 和 B 到 C 的连接时出现错误

javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
    at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2011)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1113)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1363)
    at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:916)
    at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.BufferedReader.fill(BufferedReader.java:161)
    at java.io.BufferedReader.readLine(BufferedReader.java:324)
    at java.io.BufferedReader.readLine(BufferedReader.java:389)
    at VfSer.main(VfSer.java:51)

这是我尝试过的代码,我为 A 到 B 和 B 到 C 连接使用了两个不同的证书。

这里是A码

            System.setProperty("javax.net.ssl.trustStore", "vaserkey");
            System.setProperty("javax.net.ssl.trustStoreSSN", "123456");
        try {
            SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(args[0], Integer.parseInt(args[1]));
//.....
}

这里是B码

            System.setProperty("javax.net.ssl.keyStore", "vaserkey");
            System.setProperty("javax.net.ssl.keyStorePassword", "123456");
            //adding ssl layer to socket connection
            SSLServerSocketFactory sslserversocketfactory =(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
            SSLServerSocket sslserversocket =(SSLServerSocket) sslserversocketfactory.createServerSocket(Integer.parseInt(args[0]));
            //taking infinite connection one by one
            while(true){
            //accepting incoming connection
            SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();//....
}catch(Exception e){}
        //after receiving data trying to establish connection with C
            System.setProperty("javax.net.ssl.trustStore", "vfserkey");
            System.setProperty("javax.net.ssl.trustStoreSSN", "123456");
            SSLSocketFactory sslsocketfactory1 = (SSLSocketFactory) SSLSocketFactory.getDefault();
            SSLSocket sslsocket1 = (SSLSocket) sslsocketfactory1.createSocket(ip, port);

//..向C发送数据 这是 C 代码:

        System.setProperty("javax.net.ssl.keyStore", "vfserkey");
        System.setProperty("javax.net.ssl.keyStorePassword", "123456");
    try {
        //adding ssl layer to socket connection
        SSLServerSocketFactory sslserversocketfactory =(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
        SSLServerSocket sslserversocket =(SSLServerSocket) sslserversocketfactory.createServerSocket(Integer.parseInt(args[0]));

      SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();

//获取连接并从C接收数据 } 最后是我用来生成证书的工具。

keytool -genkey vaserkey -keyalg RSA
keytool -genkey vfserkey -keyalg RSA

对不起,如果我说错了什么我是 SSL 编程的新手,如果我错了请纠正我..

首先设置所有 SSL 属性。 DefaultSSLContext 默认情况下由 SSLSocketFactorySSLServerSocketFactory 使用,但它仅构造第一次使用。更改任何相关系统属性或它们指向的文件,after 首次使用将被忽略并且无效。所以在调用either factory.

之前需要设置alljavax.net.ssl.*属性

还有:

无法正确复制这些 keytool 命令。每个文件的文件名前都需要 -keystore。他们生成私钥 (自签名)证书;你不能只生成一个证书,如果可以的话,它几乎不能用于任何东西,包括 SSL/TLS。 最后,现在首选的操作是-genkeypair;拼写 -genkey 支持向后兼容,但最终可能会被删除。

将密钥库(条目)用作信任库是不明智的。虽然 TrustManager 逻辑确实在私钥条目中找到并使用证书(见上文),但复制密钥库(或仅将私钥 entry/ies) 发送给依赖者/客户是不好的做法。 [edit] 任何获得您私钥的人通常都可以冒充您,其结果包括窃取您的敏感数据和其他资源,例如金钱。即使特定的客户端不是恶意的,他们也很少有动力像您一样小心您的密钥,如果有很多客户端,他们可能有多台机器、备份、替换、虚拟化、外包、测试、临时工、承包商等,这种风险成倍增加。

correct/better方式是only从server keystore导出证书,only导入证书到客户端信任库(采用 "Java KeyStore" 格式,但实际上不是密钥)类似于:

 keytool -keystore vaserkey -exportcert -file vasercert
 keytool -keystore vasertrust -importcert -file vasercert
 # same again for vf...
 # use vasertrust for A-trust and vfserTrust for B-trust in your example

我在这里发布可能对其他人有帮助的答案。

我按照@dave_thompson_085

的建议修改了代码

Set all SSL properties first. DefaultSSLContext is used by default by both SSLSocketFactory and SSLServerSocketFactory, but it is constructed only the first time is it used. Changing any of the relevant system properties, or the files they point to, after that first use is ignored and has no effect. So you need to set all javax.net.ssl.* properties before calling either factory.

i replaced 
SSLServerSocketFactory sslserversocketfactory =(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
with 
SSLServerSocketFactory sslserversocketfactory =(SSLServerSocketFactory)SSLContext.getDefault().getServerSocketFactory();

这里是A码

            System.setProperty("javax.net.ssl.trustStore", "vaserkey");
            System.setProperty("javax.net.ssl.trustStoreSSN", "123456");
        try {
            SSLSocketFactory sslsocketfactory =  (SSLSocketFactory) SSLContext.getDefault().getSocketFactory();
            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(args[0], Integer.parseInt(args[1]));
//.....
}

这里是B码

            System.setProperty("javax.net.ssl.keyStore", "vaserkey");
            System.setProperty("javax.net.ssl.keyStorePassword", "123456");
            //adding ssl layer to socket connection
            SSLServerSocketFactory sslserversocketfactory =SSLServerSocketFactory) SSLContext.getDefault().getServerSocketFactory();                SSLServerSocket sslserversocket =(SSLServerSocket) sslserversocketfactory.createServerSocket(Integer.parseInt(args[0]));
            //taking infinite connection one by one
            while(true){
            //accepting incoming connection
            SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();//....
}catch(Exception e){}
        //after receiving data trying to establish connection with C
            System.setProperty("javax.net.ssl.trustStore", "vfserkey");
            System.setProperty("javax.net.ssl.trustStoreSSN", "123456");
            SSLSocketFactory sslsocketfactory1 = (SSLServerSocketFactory) SSLContext.getDefault().getServerSocketFactory();
            SSLSocket sslsocket1 = (SSLSocket) sslsocketfactory1.createSocket(ip, port);

//..发送数据到C 这是 C 代码:

        System.setProperty("javax.net.ssl.keyStore", "vfserkey");
        System.setProperty("javax.net.ssl.keyStorePassword", "123456");
    try {
        //adding ssl layer to socket connection
        SSLServerSocketFactory sslserversocketfactory =((SSLServerSocketFactory)SSLContext.getDefault().getServerSocketFactory();           SSLServerSocket sslserversocket =(SSLServerSocket) sslserversocketfactory.createServerSocket(Integer.parseInt(args[0]));

      SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();//getting the connection and receiving the data from C}