[英]Connect to websocket with self-signed certificate in java
I need to use Java to connect to a WebSocket server that is using a self-signed certificate.我需要使用 Java 连接到使用自签名证书的 WebSocket 服务器。 I'm trying to use the Jetty library and am pretty new at Java but I am finding it very difficult to figure out what needs to be done.
我正在尝试使用 Jetty 库,并且在 Java 方面还是很新的,但我发现很难弄清楚需要做什么。 I can connect using NodeJS very simply:
我可以非常简单地使用 NodeJS 进行连接:
const WebSocket = require('ws');
const ws = new WebSocket('wss://192.168.100.220:9000/', ['ws-valence'], {
rejectUnauthorized: false,
});
However, modifying the example I found on the Jetty docs doesn't get me very far.但是,修改我在Jetty 文档上找到的示例并没有让我走得很远。
I implemented a basic client that works well with an echo test server, like in the example linked above.我实现了一个与回声测试服务器配合良好的基本客户端,就像上面链接的例子一样。 Then I went on to configure it with my own protocol and IP Address:
然后我继续用我自己的协议和 IP 地址配置它:
private static void connectToBasestation() {
// String destUri = "ws://echo.websocket.org";
String basestationUri = "wss://192.168.100.220:9000/";
SslContextFactory ssl = new SslContextFactory(); // ssl config
ssl.setTrustAll(true); // trust all certificates
WebSocketClient client = new WebSocketClient(ssl); // give ssl config to client
BasestationSocket socket = new BasestationSocket();
ArrayList<String> protocols = new ArrayList<String>();
protocols.add("ws-valence");
try
{
client.start();
URI bsUri = new URI(basestationUri);
ClientUpgradeRequest request = new ClientUpgradeRequest();
request.setSubProtocols(protocols);
client.connect(socket, bsUri, request);
System.out.printf("Connecting to : %s%n", bsUri);
// wait for closed socket connection.
socket.awaitClose(5,TimeUnit.SECONDS);
}
catch (Throwable t)
{
t.printStackTrace();
}
finally
{
try
{
client.stop();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
However, I'm getting an UpgradeException
with 0 null
as the values and my onConnect
method is never getting called.但是,我收到了一个
UpgradeException
0 null
的UpgradeException
,并且我的onConnect
方法永远不会被调用。 I'm guessing this is a security issue, but I can't be certain since the server is an old machine -- a bit of a black box.我猜这是一个安全问题,但我不能确定,因为服务器是一台旧机器——有点像黑匣子。 But I'm thinking maybe something is wrong with my approach?
但我在想也许我的方法有问题? Can anyone lend any advice here?
任何人都可以在这里提供任何建议吗?
Edit 1: Included trustful SSL factory as suggested.编辑 1:按照建议包含可信任的 SSL 工厂。 It did not change anything, including the stack trace from below.
它没有改变任何东西,包括下面的堆栈跟踪。
Edit 3: There is a similar question listed above, but this is different since 1) I'm getting a different error code and 2) Adding a trustful SSL factory does not solve the issue.编辑 3:上面列出了一个类似的问题,但这是不同的,因为 1) 我收到了不同的错误代码 2) 添加可信任的 SSL 工厂并不能解决问题。
Edit 2: Here is the stack trace I am getting from my OnError below:编辑 2:这是我从下面的 OnError 中获得的堆栈跟踪:
Caused by: javax.net.ssl.SSLException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1666)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1634)
at sun.security.ssl.SSLEngineImpl.recvAlert(SSLEngineImpl.java:1800)
at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:1083)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:907)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:681)
at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.process(HttpReceiverOverHTTP.java:128)
at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.receive(HttpReceiverOverHTTP.java:73)
at org.eclipse.jetty.client.http.HttpChannelOverHTTP.receive(HttpChannelOverHTTP.java:133)
at org.eclipse.jetty.client.http.HttpConnectionOverHTTP.onFillable(HttpConnectionOverHTTP.java:155)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:291)
at org.eclipse.jetty.io.ssl.SslConnection$3.succeeded(SslConnection.java:151)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
... 3 more
org.eclipse.jetty.websocket.api.UpgradeException: 0 null
at org.eclipse.jetty.websocket.client.WebSocketUpgradeRequest.onComplete(WebSocketUpgradeRequest.java:522)
at org.eclipse.jetty.client.ResponseNotifier.notifyComplete(ResponseNotifier.java:216)
at org.eclipse.jetty.client.ResponseNotifier.notifyComplete(ResponseNotifier.java:208)
at org.eclipse.jetty.client.HttpReceiver.terminateResponse(HttpReceiver.java:470)
at org.eclipse.jetty.client.HttpReceiver.abort(HttpReceiver.java:552)
at org.eclipse.jetty.client.HttpChannel.abortResponse(HttpChannel.java:156)
at org.eclipse.jetty.client.HttpSender.terminateRequest(HttpSender.java:381)
at org.eclipse.jetty.client.HttpSender.abort(HttpSender.java:566)
at org.eclipse.jetty.client.HttpSender.anyToFailure(HttpSender.java:350)
at org.eclipse.jetty.client.HttpSender$CommitCallback.failed(HttpSender.java:717)
at org.eclipse.jetty.client.http.HttpSenderOverHTTP$HeadersCallback.failed(HttpSenderOverHTTP.java:310)
at org.eclipse.jetty.io.WriteFlusher$PendingState.fail(WriteFlusher.java:263)
at org.eclipse.jetty.io.WriteFlusher.onFail(WriteFlusher.java:516)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint$FailWrite.run(SslConnection.java:1251)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:762)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:680)
at java.lang.Thread.run(Thread.java:748)
A TLS/SSL handshake error is rather generic. TLS/SSL 握手错误相当普遍。
You don't know what part of the TLS/SSL handshake the issue occurred in.您不知道问题发生在 TLS/SSL 握手的哪个部分。
You can use
-Djavax.net.debug=all
command line option on Java to see the raw details of the TLS/SSL handshake, and this might be a good place to start troubleshooting your issues with.您可以在 Java 上使用
-Djavax.net.debug=all
命令行选项来查看 TLS/SSL 握手的原始详细信息,这可能是开始解决问题的好地方。
Some options ...一些选择...
If you connect to a server and the provided certificate does not match the hostname you used in the URI to connect, this is a violation of the endpoint identification algorithm present in Java itself.如果您连接到服务器并且提供的证书与您在 URI 中用于连接的主机名不匹配,则这违反了 Java 本身中存在的端点识别算法。
Example Scenario:示例场景:
wss://192.168.1.0:8443/chat
wss://192.168.1.0:8443/chat
chatserver.acme.com
chatserver.acme.com
This is a violation, as the hostname in the URI 192.168.1.0
does not match the certificate chatserver.acme.com
这是违规行为,因为 URI
192.168.1.0
中的主机名与证书chatserver.acme.com
不匹配
This is especially common when testing with wss://localhost
or wss://127.0.0.1
这在使用
wss://localhost
或wss://127.0.0.1
测试时尤其常见
You can tell Java to not perform the Endpoint Identification check like this ...你可以告诉 Java 不要像这样执行端点识别检查......
SslContextFactory.Client ssl = new SslContextFactory.Client(); // ssl config
ssl.setEndpointIdentificationAlgorithm(null); // disable endpoint identification algorithm.
WebSocketClient client = new WebSocketClient(ssl); // give ssl config to client
⚠️ WARNING: This is not recommended, and can easily allow for man-in-the-middle attacks! ⚠️警告:不推荐这样做,很容易被中间人攻击!
Try enabling trust for all certificates.尝试为所有证书启用信任。
Example (assuming Jetty 9.4.19.v20190610 or newer):示例(假设 Jetty 9.4.19.v20190610 或更新版本):
SslContextFactory.Client ssl = new SslContextFactory.Client(); // ssl config
ssl.setTrustAll(true); // trust all certificates
WebSocketClient client = new WebSocketClient(ssl); // give ssl config to client
⚠️ WARNING: This is not recommended, and can easily allow for man-in-the-middle attacks! ⚠️警告:不推荐这样做,很容易被中间人攻击!
The algorithm used to create the certificate will limit the available Cipher Suites made available during the TLS/SSL handshake.用于创建证书的算法将限制在 TLS/SSL 握手期间可用的可用密码套件。
For example, If the server only had a DSA certificate (known vulnerable), then none of the RSA or ECDSA certificates would be available.例如,如果服务器只有一个 DSA 证书(已知易受攻击的证书),那么 RSA 或 ECDSA 证书都不可用。
The number of bits used to create the certificate is also relevant, as if the server certificate had too few, then Java itself will reject it.用于创建证书的位数也是相关的,好像服务器证书太少了,那么 Java 本身就会拒绝它。
If you are in control of the server certificate, make sure you have generated a certificate that contains both a RSA and ECDSA certificate, with at least 2048 bits for RSA (or more), and 256 bits for ECDSA.如果您控制服务器证书,请确保您已生成包含 RSA 和 ECDSA 证书的证书,其中至少 2048 位用于 RSA(或更多),256 位用于 ECDSA。
Try an empty Cipher Suite exclusion list on the Jetty side.在 Jetty 端尝试一个空的密码套件排除列表。
⚠️ WARNING: This lets you use KNOWN vulnerable Cipher Suites! ⚠️ 警告:这让您可以使用已知的易受攻击的密码套件!
Example (assuming Jetty 9.4.19.v20190610 or newer):示例(假设 Jetty 9.4.19.v20190610 或更新版本):
SslContextFactory.Client ssl = new SslContextFactory.Client(); // ssl config
ssl.setExcludeCipherSuites(); // blank out the default excluded cipher suites
WebSocketClient client = new WebSocketClient(ssl); // give ssl config to client
⚠️ WARNING: This is not recommended, and any modern computer (even cell phones) can easily read your encrypted traffic ⚠️警告:不建议这样做,任何现代计算机(甚至手机)都可以轻松读取您的加密流量
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.