【问题标题】:How to connect to an SSL Server in Java that doesn't send a certificate?如何连接到不发送证书的 Java 中的 SSL 服务器?
【发布时间】:2015-04-08 10:39:36
【问题描述】:

我的情况是 Open SSL 服务器在没有证书的情况下运行,如下所示: openssl s_server -accept 4443 -nocert

如何使用 Java 连接到此服务器?另外,如何在 Java 中启动我的服务器以不接受证书?

我已经附上了我一直在使用的 Client.java 和 Server.java 的代码。

在客户端运行附加代码时出错:

   javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    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:1959)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1077)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
    at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:702)
    at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:122)
    at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
    at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
    at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:295)
    at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141)
    at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)
    at Client.main(Client.java:27)

服务器端错误:

ERROR
140387701290656:error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher:s3_srvr.c:1358:
shutting down SSL
CONNECTION CLOSED
ACCEPT

Client.java

public class Client {
public static void main(String[] arstring) {
    try {
        SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
        SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("localhost", 4443);            
        //sslsocket.setEnabledCipherSuites(sslsocket.getSupportedCipherSuites());
        sslsocket.setEnabledCipherSuites(new String[] { "TLS_DH_anon_WITH_AES_128_CBC_SHA" }); //Added upon suggestion from Steffan

        boolean test = sslsocket.isConnected();
        System.out.println(test);
        InputStream inputstream = sslsocket.getInputStream();
        InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
        BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
        System.out.println("1");
        OutputStream outputstream = sslsocket.getOutputStream();
        System.out.println("2");
        OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream,"UTF-8");
        String str = "hello\n";
        outputstreamwriter.write(str,0,str.length());
        //outputstreamwriter.flush();
        BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter);

        String string = null;
        int count = 0;
        string = bufferedreader.readLine();
        while (true) {
            System.out.println(string);                
            System.out.flush();
            System.out.println(++count);
            string = bufferedreader.readLine();
            if (inputstream.available()==0)
                break;
            else{
                System.out.print("StreamData:");
                System.out.println(inputstream.available());
            }


        }
        System.out.println("Out");
    } catch (Exception exception) {
        exception.printStackTrace();
    }
}
}

服务器.java

public class Server {
public static  void main(String[] arstring) {
    try {
        SSLServerSocketFactory sslServersocketFactory =
                (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
        SSLServerSocket sslServerSocket =
                (SSLServerSocket) sslServersocketFactory.createServerSocket(4443);
        SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
        sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());

        if (sslSocket.isConnected())
            System.out.println("Connected to a client");

        InputStream inputStream = sslSocket.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String command = "";
        String response = "";

        while ((command = bufferedReader.readLine()) != null) {

                System.out.println("Command Recieved: "+command);

                if (command.toLowerCase().compareTo("hello")==0)
                    response = "Bingo !! You got it write!\n";
                else
                    response = "Type Hello to see a response\n";


                OutputStream outputStream = sslSocket.getOutputStream();
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream,"UTF-8");
                outputStreamWriter.write(response, 0, response.length());
                outputStreamWriter.flush();
        }
    } catch (Exception exception) {
        exception.printStackTrace();
    }
}

}

编辑:

我想我需要使用匿名 DH Cipher,但它仍然无法正常工作。可能是我做错了。代码也被修改了。

【问题讨论】:

  • 你必须使用 ADH 密码,见Java SSL/TLS with Anonymous Diffie Hellman
  • 但如果您不希望它安全,为什么还要使用 SSL?
  • @SteffenUllrich :您能否提供有关如何设置该密码的代码示例的链接?或者可能编辑我的代码并在那里显示?我试过了,但它给了 handshake_failure 我也对上面的代码进行了修改。
  • @user3667569:链接的问题包含 cmets 中已经存在的此类代码。
  • 除非明确给出密码 s_server 使用不包括 ADH 密码的集合 DEFAULT。试试openssl s_server -accept 4443 -nocert -cipher ADH-AES128-SHA

标签: java ssl openssl ssl-certificate


【解决方案1】:

如何连接到不发送证书的 Java 中的 SSL 服务器?

如果它不发送证书,那么它可能使用 (1) 预共享密钥密码套件之一,如 TLS-SRP 或 TLS-PSK,或 (2) 它使用匿名 Diffie-Hellman。

预共享密钥密码套件通常是选择,因为它们提供相互身份验证和通道绑定。

匿名 Diffie-Hellman 通常是一个糟糕的选择,因为它缺少服务器身份验证。


我的情况是 Open SSL 服务器在没有证书的情况下运行,如下所示:openssl s_server -accept 4443 -nocert

好吧,你有它。


如何使用 Java 连接到此服务器?另外,如何在 Java 中启动我的服务器以不接受证书?

使用包含匿名交换的密码套件,例如:

  • ADH-DES-CBC3-SHA
  • ADH-AES128-SHA
  • ADH-AES256-SHA
  • ADH-CAMELLIA128-SHA
  • ADH-CAMELLIA256-SHA
  • ADH-SEED-SHA
  • ADH-AES128-SHA256
  • ADH-AES256-SHA256
  • ADH-AES128-GCM-SHA256
  • ADH-AES256-GCM-SHA384

您可以在openssl ciphers 手册页中获取ADH-AES128-SHATLS_DH_anon_WITH_AES_128_CBC_SHA 的映射。

你可以试试这个命令:

openssl s_server -accept 4443 -tls1 -nocert -cipher "HIGH"

通过启用 TLS1,您可以避开任何潜在问题,即一方或另一方禁用 SSLv2 和 SSLv3(这是一件好事),但您只提供 SSLv3(这是一件坏事)。

密码套件列表字符串"HIGH"默认包含ADH(在生产环境中,通常使用"HIGH:!ADH:!RC4:!MD5...")。

【讨论】:

  • 谢谢,它清除了很多...但是我一直在提供一个场景,专门用于连接到运行此openssl s_server -accept 4443 -nocert 的服务器 客户端没有其他信息,我当然不能指定任何启动服务器时的密码
  • @user3667569 如果您不在服务器端指定任何密码,我认为您永远无法连接到这样的服务器。您甚至无法使用openssl s_client -connect localhost:4443 -cipher ALL 连接到它。
  • @Bruno 是的,我也试过了,但绝对没有漏洞吗?我希望有,因为很大程度上取决于此
  • @user3667569 默认情况下不启用匿名密码套件通常是由设计完成的(或多或少as recommended by the specification)。 SSL/TLS 实现中通常不欢迎漏洞:它们实际上被称为漏洞。听起来无论是谁为您设置了 s_server,您都不太确定他们在做什么,他们将不得不重新考虑(错误发生)。当然,设置证书比使用匿名密码套件要好得多(即使它是自签名的,你至少可以以某种方式固定它)。
  • @Bruno 我不得不说,我同意你的观点,但是如果以openssl s_server -accept 4443 -nocert 启动时无法连接服务器,那么为什么这仍然是一个有效的命令?我的意思是不应该禁止它吗?
猜你喜欢
  • 2016-07-12
  • 2017-09-26
  • 2012-12-04
  • 2012-04-02
  • 2022-01-21
  • 2011-09-22
  • 2010-09-08
  • 1970-01-01
  • 2018-08-31
相关资源
最近更新 更多