0

考虑以下代码,用于非 SSL 套接字服务器和客户端都在一个线程上:

import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerClient {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(0); // open a random free port.

        Socket c = new Socket(ss.getInetAddress(), ss.getLocalPort());

        Socket s = ss.accept();

        final byte[] bytes = "Hello World!".getBytes();
        final OutputStream out = c.getOutputStream();
        System.out.println("writing to stream");
        out.write(bytes.length);
        out.write(bytes);

        System.out.println("reading from stream");

        final DataInputStream in = new DataInputStream(s.getInputStream());
        int len = in.read();
        final byte[] b = new byte[len];
        in.readFully(b);
        System.out.println(new String(b));

        c.close();
        ss.close();
    }
}

这会产生以下输出:

writing to stream
reading from stream
Hello World!

这个过程打开了一个服务器套接字 - 与一个客户端套接字连接。将数据传递到套接字然后关闭。传递数据没有问题。

考虑一个版本来证明 SSL 套接字:

import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.logging.Logger;

public class SSLServerClient {

    private static Logger log = Logger.getLogger("InfoLogging");

    public static void main(String[] args) throws IOException {

        System.setProperty("javax.net.ssl.keyStore", "/path/KeyStore.jks");
        System.setProperty("javax.net.ssl.keyStorePassword", "password");

        SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

        SSLServerSocket serverListeningSSLSocket = (SSLServerSocket) sslserversocketfactory.createServerSocket(4380);
        log.info("Server started");

        SSLSocketFactory sslSocketFactory=(SSLSocketFactory) SSLSocketFactory.getDefault();
        SSLSocket clientSocket =  (SSLSocket) sslSocketFactory.createSocket(serverListeningSSLSocket.getInetAddress(),
                serverListeningSSLSocket.getLocalPort());

        SSLSocket serverCommsSSLSocket = (SSLSocket) serverListeningSSLSocket.accept();
        log.info("new client");

        final byte[] bytes = "Hello World!".getBytes();
        final OutputStream out = clientSocket.getOutputStream();
        System.out.println("writing to stream");
        out.write(bytes.length);
        out.write(bytes);

        System.out.println("reading from stream");

        final DataInputStream in = new DataInputStream(serverCommsSSLSocket.getInputStream());
        int len = in.read();
        final byte[] b = new byte[len];
        in.readFully(b);
        System.out.println(new String(b));

        clientSocket.close();
        serverCommsSSLSocket.close();
        serverListeningSSLSocket.close();
    }
}

这给出了以下输出:

Nov 21, 2018 10:23:51 PM com.gamble.ssl.SSLServerClient main INFO: Server started
Nov 21, 2018 10:23:52 PM com.gamble.ssl.SSLServerClient main INFO: new client
writing to stream

即它在服务器套接字启动时阻塞。

我的问题是:为什么 Java 中的 SSL 服务器套接字连接会阻塞,而非 SSL 服务器套接字却不会?

4

2 回答 2

0

这个问题的简单答案是:

因为 SSL 握手是异步的,非 SSL 套接字不必执行该步骤。

好的 - 原始 SSL 代码有两个问题:

  1. 没有 TrustStore:

    System.setProperty( "javax.net.ssl.trustStore", "/path/KeyStore.jks");
    
  2. 因为握手是同步的——你必须在不同的线程上启动客户端。IE:

    (new Thread() {
      public void run() {
        // do stuff
      }
     }).start();
    

所以代码看起来像:

    import javax.net.ssl.SSLServerSocket;
    import javax.net.ssl.SSLServerSocketFactory;
    import javax.net.ssl.SSLSocket;
    import javax.net.ssl.SSLSocketFactory;
    import java.io.DataInputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.logging.Logger;

    public class SSLServerClient {

        public static void main(String[] args) throws IOException {

            System.setProperty("javax.net.ssl.keyStore", "/path/KeyStore.jks");
            System.setProperty( "javax.net.ssl.trustStore", "/path/KeyStore.jks");

            System.setProperty("javax.net.ssl.keyStorePassword", "password");

            SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

            SSLServerSocket serverListeningSSLSocket = (SSLServerSocket) sslserversocketfactory.createServerSocket(4380);
            System.out.println("--server started");

            SSLSocketFactory sslSocketFactory=(SSLSocketFactory) SSLSocketFactory.getDefault();
            SSLSocket clientSocket =  (SSLSocket) sslSocketFactory.createSocket(serverListeningSSLSocket.getInetAddress(),
                    serverListeningSSLSocket.getLocalPort());

            SSLSocket serverCommsSSLSocket = (SSLSocket) serverListeningSSLSocket.accept();
            System.out.println("--new client");

            final byte[] bytes = "--Hello World!".getBytes();
            final OutputStream out = clientSocket.getOutputStream();
            System.out.println("--Gotten output stream");
            final DataInputStream in = new DataInputStream(serverCommsSSLSocket.getInputStream());

            (new Thread() {
                public void run() {
                    System.out.println("--reading from stream");

                    int len = 0;
                    try {
                        len = in.read();
                        final byte[] b = new byte[len];
                        in.readFully(b);
                        System.out.println(new String(b));

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();


            System.out.println("--writing to stream");
            out.write(bytes.length);
            System.out.println("--writing to stream - length");
            out.write(bytes);

            clientSocket.close();
            serverCommsSSLSocket.close();
            serverListeningSSLSocket.close();
        }
    }

输出看起来像

    --server started
    --new client
    --Gotten output stream
    --writing to stream
    --reading from stream
    --writing to stream - length
    --Hello World!

    Process finished with exit code 0

完毕

(注意,我在输出中添加了前导--以帮助读取 SSL 调试输出。

于 2018-11-22T11:09:01.080 回答
0

“翻转为明文”是什么意思?意思是?

这就是你正在做的事情:

  • 创建 SSL 服务器套接字
  • 创建连接到 SSL 服务器套接字的普通套接字
  • 从客户端发送一些数据
  • 在服务器端读取一些数据

现在,SSL 套接字需要加密数据传输。但是您未发送的数据未加密,因此它不是有效的 SSL,它会引发异常 - 正如它所说,“无法识别的 SSL 消息”,因为您没有发送有效的 SSL 消息,以及“明文连接?” 是关于可能出错的提示。

您必须在连接的两端都使用 SSL,否则它们将无法正常通信。

于 2018-11-21T00:14:04.067 回答