【问题标题】:java secure socket without authentication?没有身份验证的java安全套接字?
【发布时间】:2011-06-22 06:59:36
【问题描述】:

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

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_* 密码套件,则不需要身份验证,因此没有例外。
这是正确的吗?
因为我仍然遇到异常。我尝试在已启用的密码套件中包含所有已启用的+_anon 密码套件。没有成功。我尝试只设置匿名的并得到一个新的例外:

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

有人可以解释为什么我使用匿名密码套件会出现这些异常吗?
注意:
如果我在客户端上设置系统属性javax.net.ssl.trustStore 指向我创建并由我的服务器使用的密钥库,则通信正常!
该程序正常工作,数据从客户端发送到服务器正常。


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

String[] supported = server.getSupportedCipherSuites();
String[] anonCipherSuitesSupported = new String[supported.length];
int count = 0;

for(int i = 0; i < supported.length; i++)
{
    if(supported[i].indexOf("_anon_") > 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.<init>(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLServerSocketImpl.setEnabledCipherSuites(Unknown Source)
    at com.server.SecureOrderTaker.main(SecureOrderTaker.java:82)

谢谢

【问题讨论】:

    标签: java ssl keytool jsse truststore


    【解决方案1】:

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

    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<String> list= new ArrayList<String>();
    
    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]);
    

    【讨论】:

    • 你是对的!如果我只在客户端和服务器中设置启用匿名密码套件,按照你说的更改代码,它可以工作。但是对于我放置启用匿名密码的部分套件与当前启用的套件一起,数组大小没有问题。但它不起作用。这是否意味着非匿名密码优先?
    • 是的,我想是的。我不确切知道服务器和客户端如何选择密码,但我很确定当非匿名密码可用时,它们比匿名密码更受欢迎。它还可能取决于列表中密码套件的顺序。
    【解决方案2】:

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

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

    这意味着您的服务器证书应该在您的客户端的信任库中(正如您所提到的,指向它可以解决问题)。那个,或者 CA 的证书(更好的选择)。

    查看here,了解有关 SSL/TLS 的一般详细信息。

    如果您处理的是相互信任问题,那么您的服务器也应该信任颁发您的客户端证书的 CA。

    【讨论】:

    • 我正在启用匿名密码。所以不会有证书身份验证/验证。至少这是我所期望的。
    猜你喜欢
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    • 2021-07-23
    • 2010-09-16
    • 1970-01-01
    • 1970-01-01
    • 2015-03-08
    相关资源
    最近更新 更多