java安全套接字没有认证?

我有一个简单的安全套接字服务器 – 客户端程序。
对于服务器证书,我使用keytool创建了一个密钥库。
当我尝试通过我的客户端连接到服务器时,我得到以下exception:
在服务器中:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown 

在客户端:

 Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source) 

如果我的理解是正确的,那么这些例外是由于我使用我创建的证书这一事实引起的。
我的问题如下:
如果我在服务器和客户端中设置启用的密码套件,所有* _anon *密码套件,这不应该解决问题吗?
我的意思是如果我启用*_anon_*密码套件,则不需要身份validation,因此没有例外。
它是否正确?
因为我仍然有例外。 我尝试在启用的密码套件中启用所有已启用的+ _anon密码套件。 没有成功。 我尝试只设置anon并获得一个新例外:

 Exception in thread "main" java.lang.IllegalArgumentException: Name must not be null 

有人可以用anon密码套件解释为什么我会得到这些例外吗?
注意:
如果我在客户端上设置系统属性javax.net.ssl.trustStore指向我创建并由我的服务器使用的密钥库,通信就可以了!
该程序无exception,数据从客户端发送到服务器。


更新:
这是我用来启用匿名密码的片段(我已经为服务器和客户端部分做了这个):

 String[] supported = server.getSupportedCipherSuites(); String[] anonCipherSuitesSupported = new String[supported.length]; int count = 0; for(int i = 0; i  0) { anonCipherSuitesSupported[count++] = supported[i]; } } String[] oldEnabled = server.getEnabledCipherSuites(); String[] newEnabled = new String[oldEnabled.length + count]; System.arraycopy(oldEnabled, 0, newEnabled, 0, oldEnabled.length); System.arraycopy(anonCipherSuitesSupported, 0, newEnabled, oldEnabled.length, count); server.setEnabledCipherSuites(newEnabled); 

堆栈跟踪在客户端:

 Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source) at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source) at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source) at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source) at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source) at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source) at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.writeRecord(Unknown Source) at com.sun.net.ssl.internal.ssl.AppOutputStream.write(Unknown Source) at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source) at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source) at sun.nio.cs.StreamEncoder.implFlush(Unknown Source) at sun.nio.cs.StreamEncoder.flush(Unknown Source) at java.io.OutputStreamWriter.flush(Unknown Source) at com.client.SSLClient1.main(SSLClient1.java:58) Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(Unknown Source) at sun.security.validator.PKIXValidator.engineValidate(Unknown Source) at sun.security.validator.Validator.validate(Unknown Source) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown Source) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source) ... 14 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source) at java.security.cert.CertPathBuilder.build(Unknown Source) ... 20 more 

在服务器端:

 Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source) at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(Unknown Source) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(Unknown Source) at com.sun.net.ssl.internal.ssl.AppInputStream.read(Unknown Source) at com.sun.net.ssl.internal.ssl.AppInputStream.read(Unknown Source) at com.server.SecureOrderTaker.main(SecureOrderTaker.java:92) 

现在,如果我只是这样做:

 server.setEnabledCipherSuites(anonCipherSuitesSupported); 

因此, 只有启用了密码套件才能启用

 Exception in thread "main" java.lang.IllegalArgumentException: Name must not be null at com.sun.net.ssl.internal.ssl.CipherSuite.valueOf(Unknown Source) at com.sun.net.ssl.internal.ssl.CipherSuiteList.(Unknown Source) at com.sun.net.ssl.internal.ssl.SSLServerSocketImpl.setEnabledCipherSuites(Unknown Source) at com.server.SecureOrderTaker.main(SecureOrderTaker.java:82) 

谢谢

你是对的, *_anon_*密码用于完整的未经身份validation的连接(服务器和客户端都是匿名的)。 使用这些密码套件无需证书。 我写了一个小代码来测试:

 ServerSocketFactory sf = SSLServerSocketFactory.getDefault(); final SSLServerSocket socket = (SSLServerSocket)sf.createServerSocket(443); System.out.println(Arrays.toString(socket.getSupportedCipherSuites())); System.out.println(Arrays.toString(socket.getEnabledCipherSuites())); socket.setEnabledCipherSuites(new String[] {"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"}); Thread t = new Thread() { public void run() { try { Socket client = socket.accept(); client.getOutputStream().write("Hello World\n".getBytes("ASCII")); client.close(); } catch (IOException ioe) { } } }; t.start(); Thread.sleep(2000); SSLSocket client = (SSLSocket) SSLSocketFactory.getDefault().createSocket("localhost", 443); client.setEnabledCipherSuites(new String[] {"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"}); InputStream in = client.getInputStream(); byte[] data = new byte[1024]; int len = in.read(data); System.out.println(new String(data, 0, len)); 

我知道这段代码并不完美,但我成功地在客户端和服务器之间交换数据。 可能您的服务器或客户端套接字配置不正确。 你能给出完整的堆栈跟踪吗?

请注意,这些密码已被弃用,因为它们容易受到中间人攻击。

更新 :我发现了这个问题。 anonCipherSuitesSupported数组长度太长。 因此,在添加*_anon_* ,数组以一堆null值结束。 并且启用的密码列表中的实现似乎不接受null

 String[] supported = server.getSupportedCipherSuites(); List list= new ArrayList(); for(int i = 0; i < supported.length; i++) { if(supported[i].indexOf("_anon_") > 0) { list.add(supported[i]); } } String[] anonCipherSuitesSupported = list.toArray(new String[0]); 

您正在处理由您创建的证书,这意味着您是证书颁发机构。

你的问题是,为了双方握手和沟通,他们需要相互信任彼此的证书。 通过构建从证书到CA的链来建立信任,以查看特定方是否信任该CA(一些额外的步骤,例如检查证书是否未被撤销也可能就位)。 默认情况下,Java中大多数主要CA都是受信任的。 在您的情况下,您的自定义CA不是。

这意味着您的服务器证书应该在您的客户端的信任存储区中(如您所述,指出它解决了问题)。 那,或CA的证书(更好的选择)。

请查看SSL / TLS的一般详细信息。

如果您正在处理相互信任,那么您的服务器也应该信任颁发客户端证书的CA.