【问题标题】:Is it possible to change plain socket to SSLSocket?是否可以将普通套接字更改为 SSLSocket?
【发布时间】:2011-07-02 22:02:24
【问题描述】:

有一个普通的套接字服务器在端口12345上监听;

ServerSocket s = new ServerSocket(12345);

我想知道的是有可能:

  1. 如果客户端发送http请求,服务器直接处理请求,
  2. 如果客户端发送https请求,服务器将客户端套接字更改为SSLSocket?

谢谢

【问题讨论】:

    标签: java ssl


    【解决方案1】:

    是否可以更改普通套接字 到 SSLSocket?

    是的,是的。在服务器端,以下工作:

    ServerSocketFactory ssf = ServerSocketFactory.getDefault();
    ServerSocket serverSocket = ssf.createServerSocket(12345);
    
    // I've initialised an sslContext with a keystore, as you normally would.
    Socket socket = serverSocket.accept();
    SSLSocketFactory sslSf = sslContext.getSocketFactory();
    // The host name doesn't really matter, since we're turning it into a server socket
    // (No need to match the host name to the certificate on this side).
    SSLSocket sslSocket = (SSLSocket) sslSf.createSocket(socket, null,
        socket.getPort(), false);
    sslSocket.setUseClientMode(false);
    
    // Use the sslSocket InputStream/OutputStream as usual.
    

    SSLSocketFactory.createSocket(Socket, ...) 默认将现有的Socket 转换为客户端模式SSLSocket。由于握手仅在您开始读取/写入 I/O 流时才开始,因此仍然是时候使用 setUseClientMode(false) 更改模式。

    关于剩下的问题:

    我想知道的是 可能:

    • 如果客户端发送http请求,服务器处理请求 直接,
    • 如果客户端发送https请求,服务器改变客户端 套接字到 SSLSocket?

    再次,是的,这是可能的。它有时被称为“端口统一”,它是implemented in Grizzly,因此是Glassfish

    它之所以有效,是因为 HTTP 和 TLS(HTTPS 在其上工作)都是期望客户端首先对话的协议。因此,服务器可以检测客户端最初发送的是 TLS ClientHello 消息(在这种情况下它应该尝试进行 TLS 握手)还是普通的 HTTP 请求(例如 GET / HTTP/1.1...)。

    我怀疑使用SSLEngine 进行端口统一“更容易”,否则,可能很难在普通套接字上实现预读,您仍然可以通过SSLSocketFactory.createSocket(Socket, ...) 进行转换。

    请注意,这仍然是相当不寻常的。

    【讨论】:

    • 谢谢,它实际上帮助我解决了与实施 STARTTLS 略有不同的任务,尽管我仍然不确定要使用哪个证书。
    【解决方案2】:

    不可能在同一个端口上同时提供 http 和 https。 但是,如果客户端和服务器都支持它,理论上应该可以升级现有的 http 连接以使用 TLS ,请参阅RFC 2817。 SSLSocketFactory 类有一个 createSocket() 函数,可用于将现有套接字升级到 SSL/TLS。

    免责声明:我没有尝试这样做,实施 RFC 2817 可能并非易事。

    编辑: 显然我错了,就像布鲁诺写的那样,可以使用一种称为端口统一的技术在同一个端口上提供 http 和 https,该技术使用接收到的前几个字节来检测协议. Netty (JBoss) 框架中包含一个example

    【讨论】:

    • 我使用 createSocket 将普通套接字升级为 SSLSocket,它相对简单。但那是在套接字级别。我怀疑您对 RFC 2817 的看法是正确的。
    • @Soulman:可以通过端口统一在同一个端口上同时提供 HTTP 和 TLS (HTTPS)。 (我不知道有任何广泛使用的产品实现了 RFC 2817)。
    【解决方案3】:

    这里的 Socket/SSLSocket 接口不允许方便的内容识别 - 当您开始从套接字读取并看到它只是垃圾(不是纯 HTTP)时,您无法将此数据提供给包裹的新 SSLSocket一个普通的。

    您可以使用 Socket(或 SocketChannel)并查看数据,然后(如果它不是普通 HTTP 请求的开始)将相同的数据传递给 SSLEngine 对象以进行解密/加密。这意味着您必须自己处理所有加密/解密调用,这并非完全无关紧要(至少,它比简单地使用带有两个流的 SSLSocket 复杂得多——我做过一次)。

    当然,如果您这样做,您可能会更好地实现 RFC 2817 接口,而不是尝试自动内容嗅探。

    【讨论】:

    • 感谢您的回复! SSLEngine 是在 JDK1.5 中引入的,没有 SSLEngine 有没有办法做同样的事情?
    • 获取 TLS RFC 并自己实现协议(使用 JSSE 作为加密原语)。 (不过不推荐,因为这里有很多方法可以犯愚蠢的错误。)
    • @sodarfish:我知道您想要寻找除SSLEngine 之外的其他解决方案(主要是因为它很难使用),但您是否暗示您仍在使用 Java5 之前的 JDK? @Paŭlo Ebermann,实现 RFC 2817 的问题在于,据我所知,几乎没有客户端支持它,而查看数据原则上适用于任何 HTTP/HTTPS 客户端(尽管至少一个 URL 必须指定端口明确)。
    • @sodarfish:我真的希望你没有使用 pre-1.5 JDK;那现在已经真的老了。事实上,我也不建议针对 1.5 开发新代码,因为 1.6 已经在所有主要平台上推出多年。 (1.5 之前的 Java 要求表明问题上的标记也应该修改。)
    【解决方案4】:

    根据您发布的内容,我认为这两个方面都不是。

    1. 如果ServerSocket 上的侦听器没有将请求解释为 HTTP,那么没有什么魔法可以做到这一点。
    2. 如果ServerSocket 上的侦听器没有解密加密的请求,那么没有什么魔法可以做到这一点。

    您是否编写了监听ServerSocket 的代码?如果是,您可以通过查看您的代码来回答您自己的问题。

    是否有人编写了正在监听ServerSocket 的代码?如果是,您将不得不问他们。您是否提供了有关预期协议的任何信息?

    【讨论】:

    • 你实际上可以向前看,检查你得到的是一个ClientHello还是一个有效的普通HTTP请求。
    猜你喜欢
    • 2019-03-11
    • 2015-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-06
    • 2023-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多