使用Jetty和JavaScript客户端设置安全WebSocket服务器
我正在尝试使用Jetty设置一个安全的WebSocket服务器,如下所示:使用Jetty和JavaScript客户端设置安全WebSocket服务器,javascript,java,ssl,websocket,Javascript,Java,Ssl,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.
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();
}
}
}
之后,我将文件移动到C目录中,以便于使用路径
通过这种配置,我的服务器似乎可以毫无问题地启动。因此,我尝试通过我的网站连接到它,如下所示:
ws = new WebSocket("wss://localhost:8080/");
这根本不起作用。就像我写的一样,我认为我必须配置SSL证书。此外,为了创建我使用的服务器,java客户端实现了一个
truststore
。我必须为JavaScript做类似的事情吗?回答这个问题可能有点晚了,但经过多次尝试,我已经成功了
首先有几个一般性考虑:
- 作为要遵循的一般规则(对node.js也有效),您必须首先使HTTPS工作,WSS才能工作。WebSocket通过HTTP工作,因此如果您的服务器正确配置了HTTPS(或HTTP),那么向其中添加WebSocket将使WSS(或WS)工作。实际上,HTTPS/WSS和HTTP/WS可以同时工作
- 证书非常重要,因为并非所有类型的证书都能与Jetty一起使用(node.js也是如此)。因此,您必须生成Jetty将接受的证书
- 使HTTPS工作对于自签名证书也很重要,因为您可能必须首先通过HTTPS访问服务器并添加异常,然后WSS才能工作(这可能取决于浏览器) 另一个需要考虑的是上下文路径也需要正确地设置。我为HTTP和WS提供了独立的路径,比如HTTP的
/hello
和WS的/WS
。这两种方法可能都使用相同的上下文路径,但我还没有对此进行研究
以下是我遵循的步骤。 我在GitHub上安装了一个 1) 使用中的命令生成正确的自签名证书 上面的链接有一个关于如何生成正确的自签名证书的示例。(如果您有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请求-新建-密钥jetty.key-out jetty.csr
openssl pkcs12-inkey jetty.key-in jetty.crt-export-out jetty.pkcs12
keytool-importkeystore-srckeystore jetty.pkcs12-srcstoretypkcs12-destkeystore密钥库
2) 在Jetty中具有正确的HTTPS代码
网络上有一些资源展示了如何使用Jetty实现HTTPS,但对我来说,只有一个有用,就是它
3) 使用正确的代码来处理上下文
这是一个很难的例子-从Jetty文档页面的代码不适合我。
成功的是。本教程还告诉我,如果我尝试对HTTP和WS使用相同的路径,可能会发生冲突
4) 最后,为WebSocket编写正确的代码
我找到了正确的WebSocket代码。我们需要的是
本地jetty websocket示例
我想补充riverhorse的答案(我想添加一条评论,但在撰写本文时,我没有对答案发表评论所需的50个声誉)
虽然这个答案说明了如何使用自签名密钥完成此过程,但我知道了如何使用CA签名的证书
要做到这一点,我将假设你有权访问3个文件,这是你通常收到的文件
购买CA签名的证书时
注意:您可能有example.crt、example_key.txt等文件
各种各样的变化。只要有内容,那就无所谓了
匹配他们要描述的文件。换句话说,所有的
文件只是文本,所以只要它们包含
然后您可以使用该文件(或者您可以更改
姓名)
第一步是将主证书和中间证书合并到一个文件中,如下所示
请注意,顺序很重要,主证书应在中间证书之前
现在,下一步是使用example.key和新的cert-chain.txt生成pkcs12文件(然后可以将其放入密钥库)。这些步骤与riverhorse的答案非常相似
快跑
每次它要求输入密码时,请保持不变,您最终将在代码中使用它
最后一步是使用
keytool -importkeystore -srckeystore example.pkcs12 -srcstoretype PKCS12 -destkeystore keystore
如果遇到类似无法加载证书140126084731328:错误:0908F066:PEM例程:获取_头_和_数据:错误结束行:../crypto/PEM/PEM_lib.c:842
请检查此答案的底部是否有对我有效的潜在修复方法
现在,您有了jetty程序的密钥库,您可以返回riverhorse的示例,了解如何应用该密钥库
如果要检查密钥库是否具有可以运行的证书
keytool -list -v -keystore keystore > output_filename.txt
然后使用cat output_filename.txt
或nano output_filename.txt
查看内容
这些步骤来自riverhorse的答案以及下面的2个链接
在显示整体步骤方面确实很有帮助,但我直到将其与其他链接结合后才发现成功(但是您的里程数可能会有所不同,因为我很可能在第一次尝试时出错)
检查标题“通过PKCS12加载密钥和证书”,该链接通常有些过时,因为此时使用openssl命令将PKCS12加载到密钥库不是一件事,但它展示了如何最好地处理中间证书
势F
cat example.cer example.ca-bundle > cert-chain.txt
openssl pkcs12 -export -inkey example.key -in cert-chain.txt -out example.pkcs12
keytool -importkeystore -srckeystore example.pkcs12 -srcstoretype PKCS12 -destkeystore keystore
keytool -list -v -keystore keystore > output_filename.txt
-----END CERTIFICATE----------BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----