有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java使用Jetty和JavaScript客户端设置安全WebSocket服务器

我正在尝试使用Jetty设置一个安全的WebSocket服务器,如下所示:

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.server.WebSocketHandler;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;


public class WebSocketServer
{
    private Server server;
    private String host="localhost";
    private int port=8080;
    private String keyStorePath = "C:\\keystore";
    private String keyStorePassword="password";
    private String keyManagerPassword="password";
    private List<Handler> webSocketHandlerList = new ArrayList();
    MessageHandler messagehandler;

    public WebSocketServer()
    {
        System.out.println("WebSocketServer");

        server = new Server();

        // connector configuration
        SslContextFactory sslContextFactory = new SslContextFactory();
        sslContextFactory.setKeyStorePath(keyStorePath);
        sslContextFactory.setKeyStorePassword(keyStorePassword);
        sslContextFactory.setKeyManagerPassword(keyManagerPassword);
        SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString());
        HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(new HttpConfiguration());
        ServerConnector sslConnector = new ServerConnector(server, sslConnectionFactory, httpConnectionFactory);
        sslConnector.setHost(host);
        sslConnector.setPort(port);
        server.addConnector(sslConnector);

        // handler configuration
        HandlerCollection handlerCollection = new HandlerCollection();
        handlerCollection.setHandlers(webSocketHandlerList.toArray(new Handler[0]));
        server.setHandler(handlerCollection);

        WebSocketHandler wsHandler = new WebSocketHandler() {
            @Override
            public void configure(WebSocketServletFactory webSocketServletFactory) {
                webSocketServletFactory.register(MyWebSocketHandler.class);
            }
        };
        ContextHandler wsContextHandler = new ContextHandler();
        wsContextHandler.setHandler(wsHandler);
        wsContextHandler.setContextPath("/");  // this context path doesn't work ftm
        webSocketHandlerList.add(wsHandler);

        messagehandler = new MessageHandler();
        new Thread(messagehandler).start();

        try {
            server.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

密钥库文件是使用jdk/bin文件夹中的here命令创建的:

keytool.exe -keystore keystore -alias jetty -genkey -keyalg RSA

之后,我将文件移动到C目录中,以便于使用路径

通过这种配置,我的服务器似乎可以毫无问题地启动。因此,我尝试通过我的网站连接到它,如下所示:

ws = new WebSocket("wss://localhost:8080/");

这根本不起作用。与书面here一样,我认为我必须配置SSL证书。此外,为了创建服务器,我使用了这个tutorial,对于java客户端,它们实现了一个truststore。我必须为JavaScript做类似的事情吗


共 (2) 个答案

  1. # 1 楼答案

    回答可能有点晚了,但经过多次尝试,我已经成功了


    首先有几个一般性考虑:

    • 作为要遵循的一般规则(对node.js也有效),您必须首先使HTTPS工作,WSS才能工作。WebSocket通过HTTP工作,因此如果您的服务器正确配置了HTTPS(或HTTP),那么向其中添加WebSocket将使WSS(或WS)工作。实际上,HTTPS/WSS和HTTP/WS可以同时工作

    • 证书非常重要,因为并非所有类型的证书都能与Jetty一起使用(node.js也是如此)。因此,您必须生成Jetty将接受的证书

    • 使HTTPS工作对于自签名证书也很重要,因为您可能必须首先通过HTTPS访问服务器并添加异常,然后WSS才能工作(这可能取决于浏览器)

    • 另一个需要考虑的是上下文路径也需要正确地设置。我为HTTP和WS提供了独立的路径,如/hello用于HTTP(S)和/ws用于WS(S)。这两种方法可能都使用相同的上下文路径,但我还没有对此进行调查


    以下是我遵循的步骤。 我在GitHub上放了一个working example

    1)使用来自here的命令生成正确的自签名证书

    上面的链接有一个关于如何生成正确的自签名证书的示例。(如果您有CA签名的证书,我认为过程有些不同。)

    我将命令粘贴到这里以便于访问。请注意,无论何时询问,请始终提供相同的密码(包括最后一个步骤,当您键入的密码将以明文显示在屏幕上时)

    openssl genrsa -aes256 -out jetty.key
    openssl req -new -x509 -key jetty.key -out jetty.crt
    keytool -keystore keystore -import -alias jetty -file jetty.crt -trustcacerts
    openssl req -new -key jetty.key -out jetty.csr
    openssl pkcs12 -inkey jetty.key -in jetty.crt -export -out jetty.pkcs12
    keytool -importkeystore -srckeystore jetty.pkcs12 -srcstoretype PKCS12 -destkeystore keystore
    

    2)在Jetty中具有正确的HTTPS代码

    网络上有一些资源展示了如何使用Jetty实现HTTPS,但对我来说只有一个有用,那就是here

    3)具有处理上下文的正确代码

    这是一个很难的例子-从Jetty文档页面的代码不适合我。 起作用的是this。本教程还告诉我,如果我尝试对HTTP和WS使用相同的路径,可能会发生冲突

    4)最后,为WebSocket提供正确的代码

    我找到了正确的WebSocket代码here。我们所需要的是native-jetty-websocket-example

  2. # 2 楼答案

    我想补充riverhorse的答案(我想补充一条评论,但在撰写本文时,我没有对答案发表评论所需的50个声誉)

    虽然这个答案说明了如何使用自签名密钥完成此过程,但我知道了如何使用CA签名的证书

    要做到这一点,我将假设你有权访问3个文件,这是你通常收到的文件 购买CA签名的证书时

    Note: you may have files like example.crt, example_key.txt, and all kinds of variations. That will not matter as long as there contents match the file they are meant to describe. In other words, all of the files are just text, so as long they contain the required text for that specific file, then you can use that file instead (or you can change the name).

    example.cer -> the main certificate
    
    example.key -> the key for the certificate
    
    example.ca-bundle -> the intermediate certificate
    

    第一步是将主证书和中间证书合并到一个文件中,如下所示

    Note that the order is important, the main certificate should be before the intermediate certificates

    cat example.cer example.ca-bundle > cert-chain.txt
    

    现在,下一步是使用示例。密钥和新的证书链。txt生成pkcs12文件(然后可以将其放入密钥库)。这些步骤与riverhorse的答案非常相似

    快跑

    Every time it asks for a password, keep it the same, you will eventually use it in the code

    openssl pkcs12 -export -inkey example.key -in cert-chain.txt -out example.pkcs12
    

    最后一步是使用

    keytool -importkeystore -srckeystore example.pkcs12 -srcstoretype PKCS12 -destkeystore keystore
    

    If you run into an issue like unable to load certificates 140126084731328:error:0908F066:PEM routines:get_header_and_data:bad end line:../crypto/pem/pem_lib.c:842 check the bottom of this answer for a potential fix that worked for me

    现在,您有了jetty程序的密钥库,您可以返回riverhorse的示例,了解如何应用该密钥库

    如果要检查密钥库是否具有可以运行的证书

    keytool -list -v -keystore keystore > output_filename.txt
    

    然后使用cat output_filename.txtnano output_filename.txt查看内容

    这些步骤来自riverhorse的答案以及下面的2个链接

    https://www.thesslstore.com/knowledgebase/ssl-install/jetty-java-http-servlet-webserver-ssl-installation/

    Really helpful in showing the overall steps, but I did not find success until combing it with the other link (however your mileage may vary as its very possible I made a mistake in my first try)

    https://wiki.eclipse.org/Jetty/Howto/Configure_SSL#Requesting_a_Trusted_Certificate

    Check the header "Loading Keys and Certificates via PKCS12", this link in general is somewhat outdated as using the openssl command to load the pkcs12 into the keystore was not a thing at this point, but it showed how to deal with intermediate certificates the best

    加载问题的潜在修复程序:

    如果你有这个问题,一个可能的原因就是证书链。txt文件,打开它并检查两个证书之间的行是否与(5之前和之后)相同

      -END CERTIFICATE     BEGIN CERTIFICATE  -
    

    应该如此

      -END CERTIFICATE  -
      -BEGIN CERTIFICATE  -