我正试图使用Jetty(或任何其他库)连接到一个安全的websocket。
问题是我得到了一个"找不到可信证书"的错误。我使用的是由keytool生成的自签名证书。可以做些什么?
import java.net.URI;
import java.util.concurrent.Future;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.client.WebSocketClient;
public class Socket extends WebSocketAdapter{
public static void main(String[] args) {
String url = "wss://qa.sockets.stackexchange.com/"; //or "wss://echo.websocket.org"
// making sure the the jvm find keystore
String JAVASEC="C:/Program Files/Java/jdk1.8.0_25/jre/lib/security/";
System.setProperty("javax.net.ssl.keyStore", JAVASEC+"keystore.jks");
System.setProperty("javax.net.ssl.trustStore", JAVASEC+"cacerts.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
System.out.println(System.getProperty("javax.net.ssl.trustStore"));
SslContextFactory sslContextFactory = new SslContextFactory();
Resource keyStoreResource = Resource.newResource(Socket.class.getResource("/keystore.jks"));//generated with keytool
sslContextFactory.setKeyStoreResource(keyStoreResource);
sslContextFactory.setKeyStorePassword("password");
sslContextFactory.setKeyManagerPassword("password");
WebSocketClient client = new WebSocketClient(sslContextFactory);
try{
client.start();
Socket socket = new Socket();
Future<Session> fut = client.connect(socket,URI.create(url));
Session session = fut.get();
session.getRemote().sendString("Hello");
}
catch (Throwable t){
t.printStackTrace(System.err);
}
}
@Override
public void onWebSocketConnect(Session sess){
super.onWebSocketConnect(sess);
System.out.println("Socket Connected: " + sess);
}
@Override
public void onWebSocketText(String message){
super.onWebSocketText(message);
System.out.println("Received TEXT message: " + message);
}
@Override
public void onWebSocketClose(int statusCode, String reason){
super.onWebSocketClose(statusCode,reason);
System.out.println("Socket Closed: [" + statusCode + "] " + reason);
}
@Override
public void onWebSocketError(Throwable cause){
super.onWebSocketError(cause);
cause.printStackTrace(System.err);
}
}
这是Tyrus websocket客户端的一次尝试,我没有收到SSL错误,但它什么也没打印:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.CloseReason;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import org.glassfish.grizzly.ssl.SSLContextConfigurator;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.container.grizzly.GrizzlyEngine;
public class ClientWebSocketEndpoint extends Endpoint {
public static void main(String[] a) throws IOException{
ClientManager client = ClientManager.createClient();
//System.getProperties().put("javax.net.debug", "all");
final SSLContextConfigurator defaultConfig = new SSLContextConfigurator();
defaultConfig.retrieve(System.getProperties());
// or setup SSLContextConfigurator using its API.
SSLEngineConfigurator sslEngineConfigurator =
new SSLEngineConfigurator(defaultConfig, true, false, false);
client.getProperties().put(GrizzlyEngine.SSL_ENGINE_CONFIGURATOR,
sslEngineConfigurator);
Session session = null;
final ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().build();
try {
session = client.connectToServer(ClientWebSocketEndpoint.class, cec, new URI("wss://qa.sockets.stackexchange.com/"));// or "wss://echo.websocket.org"
} catch (DeploymentException | URISyntaxException e) {
e.printStackTrace();
} finally {
if (session != null && session.isOpen())
session.close(new CloseReason(CloseReason.CloseCodes.GOING_AWAY, "Bye"));
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
}
@Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MessageHandler.Whole<String>() {
@Override
public void onMessage(String message) {
System.out.println("Received message: "+message);
}
});
try {
session.getBasicRemote().sendText("1-questions-active");
session.getBasicRemote().sendText("155-questions-active");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
相比之下,JS/节点中的这个简单代码工作于
var WebSocket = require('ws')
, ws = new WebSocket('wss://qa.sockets.stackexchange.com/');//"wss://echo.websocket.org"
ws.on('message', function(message) {
console.log('received: %s', message);
});
ws.on('open', function() {
ws.send('155-questions-active');
ws.send('1-questions-active');
});
我很高兴知道Java 中有一个可以工作的websocket客户端
正确的方法是将客户端密钥库/信任库配置为信任特定的自签名服务器证书。关于stackoverflow,有很多答案演示了如何通过简单地适当配置密钥库和/或信任库文件内容来使用Java实现这一点。(无需代码)。
如果你想";仅连接";,如果您不关心信任、证书有效性,甚至端点标识验证(基本上是构成安全连接的所有好东西),那么您就不需要使用密钥库、信任库、自定义X509TrustManagers
甚至自定义SSLContexts
来连接到服务。这种需求在开发/测试/qa期间很常见,但不应在生产代码中使用。
使用Jetty 9.4.35.v20201120的方法是只配置SslContextFactory.Client
(属于HttpClient
,WebSocketClient
正在使用)来信任所有证书。
import java.net.URI;
import java.util.concurrent.Future;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.WebSocketClient;
@WebSocket
public class SecureClientSocket
{
private static final Logger LOG = Log.getLogger(SecureClientSocket.class);
public static void main(String[] args)
{
String url = "wss://qa.sockets.stackexchange.com/";
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
// Tell sslContextFactory to trust all server certificates
// This is suitable for test/qa environments, and internal environments,
// but IS NOT SUITABLE FOR PRODUCTION.
// Note: this is not actually necessary for wss://qa.sockets.stackexchange.com/
sslContextFactory.setTrustAll(true);
// If you do choose to comment out the above, this option will cause the
// Java client side SSL/TLS to validate the server certificate name
// against the URL used to connect to the server, if it doesn't match
// then the connection is not established.
sslContextFactory.setEndpointIdentificationAlgorithm("HTTPS");
HttpClient httpClient = new HttpClient(sslContextFactory);
try
{
httpClient.start();
WebSocketClient client = new WebSocketClient(httpClient);
client.start();
SecureClientSocket socket = new SecureClientSocket();
Future<Session> fut = client.connect(socket, URI.create(url));
Session session = fut.get();
session.getRemote().sendString("Hello");
session.getRemote().sendString("155-questions-active");
}
catch (Throwable t)
{
LOG.warn(t);
}
}
@OnWebSocketConnect
public void onConnect(Session sess)
{
LOG.info("onConnect({})", sess);
}
@OnWebSocketClose
public void onClose(int statusCode, String reason)
{
LOG.info("onClose({}, {})", statusCode, reason);
}
@OnWebSocketError
public void onError(Throwable cause)
{
LOG.warn(cause);
}
@OnWebSocketMessage
public void onMessage(String msg)
{
LOG.info("onMessage() - {}", msg);
}
}
会有这样的结果。。。
2020-12-21 14:03:28.228:INFO::main: Logging initialized @173ms to org.eclipse.jetty.util.log.StdErrLog
2020-12-21 14:03:28.401:WARN:oejusS.config:main: Trusting all certificates configured for Client@4b5a5ed1[provider=null,keyStore=null,trustStore=null]
2020-12-21 14:03:28.402:WARN:oejusS.config:main: No Client EndPointIdentificationAlgorithm configured for Client@4b5a5ed1[provider=null,keyStore=null,trustStore=null]
2020-12-21 14:03:28.862:INFO:j.SecureClientSocket:HttpClient@3712b94-60: onConnect(WebSocketSession[websocket=JettyAnnotatedEventDriver[jetty.SecureClientSocket@3f38289c],behavior=CLIENT,connection=WebSocketClientConnection@7556df66::DecryptedEndPoint@11c4a450{l=/192.168.1.217:46512,r=qa.sockets.stackexchange.com/198.252.206.25:443,OPEN,fill=-,flush=-,to=66/300000},remote=WebSocketRemoteEndpoint@278a12bf[batching=true],incoming=JettyAnnotatedEventDriver[jetty.SecureClientSocket@3f38289c],outgoing=ExtensionStack[queueSize=0,extensions=[],incoming=org.eclipse.jetty.websocket.common.WebSocketSession,outgoing=org.eclipse.jetty.websocket.client.io.WebSocketClientConnection]])
2020-12-21 14:03:30.648:INFO:j.SecureClientSocket:HttpClient@3712b94-62: onMessage() - {"action":"155-questions-active","data":"{"siteBaseHostAddress":"graphicdesign.stackexchange.com","id":138914,"titleEncodedFancy":"How to achieve purple, paper background texture look?","bodySummary":"How is the below purple background texture look achieved? I'm assuming it is a paper texture effect with different layers of light? However when I try to apply both the effect it never turns out the ...","tags":["texture"],"lastActivityDate":1608581010,"url":"https://graphicdesign.stackexchange.com/questions/138914/how-to-achieve-purple-paper-background-texture-look","ownerUrl":"https://graphicdesign.stackexchange.com/users/155152/homan-cheung","ownerDisplayName":"Homan Cheung","apiSiteParameter":"graphicdesign"}"}
2020-12-21 14:03:30.791:INFO:j.SecureClientSocket:HttpClient@3712b94-60: onMessage() - {"action":"155-questions-active","data":"{"siteBaseHostAddress":"unix.stackexchange.com","id":457386,"titleEncodedFancy":"IPtables logging is not working in CentOS 7","bodySummary":"I want to log all the traffic which comes in and out from the port X. I have followed below steps.\r\nEdited /etc/syslog.conf , /etc/rsyslog.conf , /etc/systemd/system/rsyslog.service.d/rsyslog.conf ...","tags":["centos","kernel","iptables","logs"],"lastActivityDate":1608581010,"url":"https://unix.stackexchange.com/questions/457386/iptables-logging-is-not-working-in-centos-7","ownerUrl":"https://unix.stackexchange.com/users/301499/karthikeyan-s","ownerDisplayName":"Karthikeyan s","apiSiteParameter":"unix"}"}
2020-12-21 14:03:32.235:INFO:j.SecureClientSocket:HttpClient@3712b94-62: onMessage() - {"action":"155-questions-active","data":"{"siteBaseHostAddress":"math.stackexchange.com","id":812563,"titleEncodedFancy":"Jacobian of exponential mapping in SO3/SE3","bodySummary":"Following this post\nJacobian matrix of the Rodrigues' formula (exponential map)\n\nWhat if I really need the Jacobian of the exponential mapping function in $\\omega \\neq 0$?\n\nBasically, I want to ...","tags":["lie-groups","3d","rotations","numerical-optimization","rigid-transformation"],"lastActivityDate":1608581012,"url":"https://math.stackexchange.com/questions/812563/jacobian-of-exponential-mapping-in-so3-se3","ownerUrl":"https://math.stackexchange.com/users/153816/user153816","ownerDisplayName":"user153816","apiSiteParameter":"math"}"}
2020-12-21 14:03:35.343:INFO:j.SecureClientSocket:HttpClient@3712b94-60: onMessage() - {"action":"155-questions-active","data":"{"siteBaseHostAddress":"raspberrypi.stackexchange.com","id":76325,"titleEncodedFancy":"GCC version for compiling a loadable kernel module","bodySummary":"I am not very familiar with Linux, so may be my question is a little bit foolish.\n\nI would like to compile kernel module without recompiling the kernel (USB Wi-Fi stick driver for MT7106U chip). I ...","tags":["kernel","modules","gcc"],"lastActivityDate":1608581015,"url":"https://raspberrypi.stackexchange.com/questions/76325/gcc-version-for-compiling-a-loadable-kernel-module","ownerUrl":"https://raspberrypi.stackexchange.com/users/77643/cyclone125","ownerDisplayName":"cyclone125","apiSiteParameter":"raspberrypi"}"}
2020-12-21 14:03:36.775:INFO:j.SecureClientSocket:JettyShutdownThread: onClose(1006, Disconnected)
好吧,我尝试使用您的代码来复制问题,但没有成功(获取证书,使用keytool命令导入证书,然后运行代码)。我的输出是这样的。
2015-03-22 23:03:16.192:INFO::main: Logging initialized @503ms
Socket Connected:WebSocketSession[websocket=JettyListenerEventDriver[com.ivan.Main],behavior=CLIENT,connection=WebSocketClientConnection@77ae29b0{IDLE}{f=Flusher[queueSize=0,aggregateSize=0,failure=null],g=Generator[CLIENT,validating],p=Parser@4e086d6d[ExtensionStack,s=START,c=0,len=0,f=null,p=WebSocketPolicy@7c0ceccc[behavior=CLIENT,maxTextMessageSize=65536,maxTextMessageBufferSize=32768,maxBinaryMessageSize=65536,maxBinaryMessageBufferSize=32768,asyncWriteTimeout=60000,idleTimeout=300000,inputBufferSize=4096]]},remote=WebSocketRemoteEndpoint@49f64[batching=true],incoming=JettyListenerEventDriver[com.ivan.Main],outgoing=ExtensionStack[queueSize=0,extensions=[],incoming=org.eclipse.jetty.websocket.common.WebSocketSession,outgoing=org.eclipse.jetty.websocket.client.io.WebSocketClientConnection]]
Received TEXT message: {"action":"155-questions-active","data":"{"siteBaseHostAddress":"cogsci.stackexchange.com","id":9404,"titleEncodedFancy":"What's the difference between repetition suppression and habituation?","bodySummary":"Neural repetition suppression seems to be describing behavioral habituation on a neuronal level. What's the difference between these two terms?\n","tags":["cognitive-neuroscience","terminology","conditioning","perceptual-learning"],"lastActivityDate":1427036607,"url":"http://cogsci.stackexchange.com/questions/9404/whats-the-difference-between-repetition-suppression-and-habituation","ownerUrl":"http://cogsci.stackexchange.com/users/7569/recursive-farts","ownerDisplayName":"RECURSIVE FARTS","apiSiteParameter":"cogsci"}"}
Received TEXT message: {"action":"155-questions-active","data":"{"siteBaseHostAddress":"mathematica.stackexchange.com","id":77933,"titleEncodedFancy":"FunctionDomain doesn't work with If","bodySummary":"When I call FunctionDomain for function defined by formule with If, for example \n\nf[x_]:=If[x<0, x, x^3], \n\nI get \"Unable to find the domain with the available methods\". What should I do to get a ...","tags":["functions"],"lastActivityDate":1427036609,"url":"http://mathematica.stackexchange.com/questions/77933/functiondomain-doesnt-work-with-if","ownerUrl":"http://mathematica.stackexchange.com/users/27150/adolzi","ownerDisplayName":"adolzi","apiSiteParameter":"mathematica"}"}
Received TEXT message: {"action":"155-questions-active","data":"{"siteBaseHostAddress":"serverfault.com","id":677390,"titleEncodedFancy":"Smart Array E200i battery on 350ML did not fix the warning message (battery failure)","bodySummary":"I have got this warning message on the System Management in a Proliant server 350ML G5.\n\nDegraded Accelerator\nName Value\nStatus: Temporarily Disabled\nError Code: Cache Disabled Low Batteries\nSerial ...","tags":["hp-proliant","hp-smart-array"],"lastActivityDate":1427036609,"url":"http://serverfault.com/questions/677390/smart-array-e200i-battery-on-350ml-did-not-fix-the-warning-message-battery-fail","ownerUrl":"http://serverfault.com/users/164557/dlopezgonzalez","ownerDisplayName":"dlopezgonzalez","apiSiteParameter":"serverfault"}"}
Received TEXT message: {"action":"155-questions-active","data":"{"siteBaseHostAddress":"askubuntu.com","id":599889,"titleEncodedFancy":"Is downgrading back to 14.04 from 14.10 possible?","bodySummary":"I am using Ubuntu 14.10 alongside Windows 7 and I want to degrade to 14.04. I made a bootable pendrive using Universal USB Installer and seems like the pendrive is working fine. On the Installer Boot ...","tags":["14.04","boot","upgrade","downgrade"],"lastActivityDate":1427036610,"url":"http://askubuntu.com/questions/599889/is-downgrading-back-to-14-04-from-14-10-possible","ownerUrl":"http://askubuntu.com/users/374332/pkj","ownerDisplayName":"pkj","apiSiteParameter":"askubuntu"}"}
Received TEXT message: {"action":"155-questions-active","data":"{"siteBaseHostAddress":"superuser.com","id":814765,"titleEncodedFancy":"External Harddrive Shutting Off and On Every So Often While Listening to Music","bodySummary":"I am always listening to music on my PC (always via VLC Player) and every so often - maybe once every 2 songs, sometimes more frequently- the music stops playing (VLC's buffer/progress bar stops ...","tags":["hard-drive","power","external"],"lastActivityDate":1427036610,"url":"http://superuser.com/questions/814765/external-harddrive-shutting-off-and-on-every-so-often-while-listening-to-music","ownerUrl":"http://superuser.com/users/338547/supercookie47","ownerDisplayName":"SuperCookie47","apiSiteParameter":"superuser"}"}
Received TEXT message: {"action":"155-questions-active","data":"{"siteBaseHostAddress":"math.stackexchange.com","id":1200778,"titleEncodedFancy":"Continuity of a piecewise function at a specific point","bodySummary":"I am having trouble proving the following function is not continuous at $x = 0$ using a formal definition of continuity.\n\n$\nf(x) = \\left\\{\n \\begin{array}{lr}\n \\sin(\\frac{1}{x}) & : x \\neq 0\\\\\n ...","tags":["real-analysis","functions","continuity","epsilon-delta"],"lastActivityDate":1427036612,"url":"http://math.stackexchange.com/questions/1200778/continuity-of-a-piecewise-function-at-a-specific-point","ownerUrl":"http://math.stackexchange.com/users/222744/george","ownerDisplayName":"George","apiSiteParameter":"math"}"}
所以我想,如果你想要一个在java中运行的web套接字客户端,我想你可以接受所有证书,比如@tinker给你的链接。
代码应该是这样的。也许你可以试试,看看你的位置会发生什么。
import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.concurrent.Future;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.client.WebSocketClient;
public class Main extends WebSocketAdapter {
public static void main(String[] args) {
String url = "wss://qa.sockets.stackexchange.com/"; // or
// "wss://echo.websocket.org"
SslContextFactory sslContextFactory = new SslContextFactory();
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
} };
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
sslContextFactory.setSslContext(sc);
WebSocketClient client = new WebSocketClient(sslContextFactory);
client.start();
Future<Session> fut = client.connect(new Main(), URI.create(url));
Session session = fut.get();
session.getRemote().sendString("Hello");
session.getRemote().sendString("155-questions-active");
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
@Override
public void onWebSocketConnect(Session sess) {
super.onWebSocketConnect(sess);
System.out.println("Socket Connected: " + sess);
}
@Override
public void onWebSocketText(String message) {
super.onWebSocketText(message);
System.out.println("Received TEXT message: " + message);
}
@Override
public void onWebSocketClose(int statusCode, String reason) {
super.onWebSocketClose(statusCode, reason);
System.out.println("Socket Closed: [" + statusCode + "] " + reason);
}
@Override
public void onWebSocketError(Throwable cause) {
super.onWebSocketError(cause);
cause.printStackTrace(System.err);
}
}
这是我的Tyrus java客户端,我使用存根连接到我的Jetty 9.3.6服务器,该服务器使用自签名证书运行HTTPS(改编自Tyrus websocket客户端,第8.1节):
client = ClientManager.createClient();
//System.getProperties().put("javax.net.debug", "all"); // Useful for debugging SSL interaction
// The keystore in the next two lines is the same keystore you used for running the server,
// likely in ${jetty.base}/etc/keystore
System.getProperties().put(SSLContextConfigurator.KEY_STORE_FILE, "/tmp/keystore");
System.getProperties().put(SSLContextConfigurator.TRUST_STORE_FILE, "/tmp/keystore");
// The following two passwords are what you used for your self-signed cert
System.getProperties().put(SSLContextConfigurator.KEY_STORE_PASSWORD, "HumanReadablePassword");
System.getProperties().put(SSLContextConfigurator.TRUST_STORE_PASSWORD, "HumanReadablePassword");
final SSLContextConfigurator defaultConfig = new SSLContextConfigurator();
defaultConfig.retrieve(System.getProperties());
// or setup SSLContextConfigurator using its API.
SSLEngineConfigurator sslEngineConfigurator = new SSLEngineConfigurator(defaultConfig, true, false, false);
client.getProperties().put(GrizzlyEngine.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
client.connectToServer(sillyWebSocketClient , ClientEndpointConfig.Builder.create().build(),
new URI("wss://localhost:8443/sillyWebSocketServer/echo"));
System.out.println ("Connected .... ");
其中SillyWebSocketClient sillyWebSocketClient
扩展了javax.websocket.Endpoint
。
我使用的是Java 8/Gradle 2.7环境,我的build.gradle
如下所示:
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'javax:javaee-api:7.0'
compile 'org.glassfish.grizzly:grizzly-core:2.3.23'
compile 'org.glassfish.tyrus:tyrus-container-grizzly:1.2.1'
compile 'org.glassfish.tyrus:tyrus-client:1.6'
compile 'javax.websocket:javax.websocket-client-api:1.1'
}
希望这能有所帮助。