繁体   English   中英

Java安全套接字无需身份验证?

[英]java secure socket without authentication?

我有一个简单的安全套接字服务器-客户端程序。
对于服务器证书,我使用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指向我创建并由服务器使用的密钥库,则通信正常!
该程序无一例外都可以正常工作,并且数据可以从客户端正确发送到服务器。


更新:
这是我用来启用匿名密码的代码段(我已经在服务器和客户端部分完成了此操作):

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)

谢谢

没错, *_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]);

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

您的问题是,为了使双方握手和沟通,他们都需要彼此信任对方的证书。 通过建立从证书到CA的链来建立信任关系,以查看特定方是否信任该CA(也可以执行一些其他步骤,例如检查证书是否被吊销)。 默认情况下,大多数主要的CA在Java中都是受信任的。 在您的情况下,您的自定义CA不是。

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

在此处查看有关SSL / TLS的一般详细信息。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM