连接到安全的websocket

我正在尝试使用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 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() { @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 / node中的这个简单代码可以工作

 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客户端

有一个更简单的方法,不需要混淆信任库,或自定义X509TrustManagers ,或自定义SSLContexts

只需使用SslContextFactory已经提供的内容……

 package jetty.websocket; import java.net.URI; import java.util.concurrent.Future; 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 sslContextFactory = new SslContextFactory(); sslContextFactory.setTrustAll(true); // The magic WebSocketClient client = new WebSocketClient(sslContextFactory); try { client.start(); SecureClientSocket socket = new SecureClientSocket(); Future 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); } } 

哪个会有这样的结果……

 2015-03-26 06:46:34.893:INFO::main: Logging initialized @62ms 2015-03-26 06:46:35.700:INFO:jw.SecureClientSocket:WebSocketClient@1849650547-15: onConnect(WebSocketSession[websocket=JettyAnnotatedEventDriver[jetty.websocket.SecureClientSocket@c299bbd],behavior=CLIENT,connection=WebSocketClientConnection@7faf9b87{IDLE}{f=Flusher[queueSize=0,aggregateSize=0,failure=null],g=Generator[CLIENT,validating],p=Parser@7a65105a[ExtensionStack,s=START,c=0,len=0,f=null,p=WebSocketPolicy@114c37d[behavior=CLIENT,maxTextMessageSize=65536,maxTextMessageBufferSize=32768,maxBinaryMessageSize=65536,maxBinaryMessageBufferSize=32768,asyncWriteTimeout=60000,idleTimeout=300000,inputBufferSize=4096]]},remote=WebSocketRemoteEndpoint@7a2e4418[batching=true],incoming=JettyAnnotatedEventDriver[jetty.websocket.SecureClientSocket@c299bbd],outgoing=ExtensionStack[queueSize=0,extensions=[],incoming=org.eclipse.jetty.websocket.common.WebSocketSession,outgoing=org.eclipse.jetty.websocket.client.io.WebSocketClientConnection]]) 2015-03-26 06:46:39.464:INFO:jw.SecureClientSocket:WebSocketClient@1849650547-14: onMessage() - {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"math.stackexchange.com\",\"id\":1207542,\"titleEncodedFancy\":\"Do we have this type of integral expression of Bessel function of the first kind?\",\"bodySummary\":\"Let $z=\\\\lambda+i\\\\mu$ with $\\\\mu>0$. Then for any $r>0$, $k=1,2,3, \\\\cdots$. Do we have the following identity\\n$$\\n\\\\int_{r}^{\\\\infty}{\\\\frac{t}{\\\\sqrt{t^2-r^2}}(\\\\frac{1}{t}\\\\frac{d}{dt})^k ...\",\"tags\":[\"analysis\",\"reference-request\",\"special-functions\"],\"lastActivityDate\":1427377599,\"url\":\"http://math.stackexchange.com/questions/1207542/do-we-have-this-type-of-integral-expression-of-bessel-function-of-the-first-kind\",\"ownerUrl\":\"http://math.stackexchange.com/users/37742/sun\",\"ownerDisplayName\":\"sun\",\"apiSiteParameter\":\"math\"}"} 2015-03-26 06:46:41.469:INFO:jw.SecureClientSocket:WebSocketClient@1849650547-16: onMessage() - {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"drupal.stackexchange.com\",\"id\":152924,\"titleEncodedFancy\":\"Features can export everything?\",\"bodySummary\":\"Built all blocks, menu, taxonomies, view etc in Drupal CMS on localhost machine, but I need to export everything to the live server. Is it possible to use features or just export sql?\\n\",\"tags\":[\"theming\"],\"lastActivityDate\":1427377601,\"url\":\"http://drupal.stackexchange.com/questions/152924/features-can-export-everything\",\"ownerUrl\":\"http://drupal.stackexchange.com/users/34551/joe\",\"ownerDisplayName\":\"joe\",\"apiSiteParameter\":\"drupal\"}"} 2015-03-26 06:46:44.034:INFO:jw.SecureClientSocket:WebSocketClient@1849650547-13: onMessage() - {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"wordpress.stackexchange.com\",\"id\":182350,\"titleEncodedFancy\":\"Create page from plugin, but have it completely hidden\",\"bodySummary\":\"I need to have a plugin create a page that is publicly accessible, but does NOT show up in the admin section anywhere, and cannot be deleted or modified.\\n\\nI thought maybe figuring out a way to have a ...\",\"tags\":[\"plugins\",\"pages\"],\"lastActivityDate\":1427377603,\"url\":\"http://wordpress.stackexchange.com/questions/182350/create-page-from-plugin-but-have-it-completely-hidden\",\"ownerUrl\":\"http://wordpress.stackexchange.com/users/45132/joel-worsham\",\"ownerDisplayName\":\"Joel Worsham\",\"apiSiteParameter\":\"wordpress\"}"} 

这是我使用存根的Tyrus-java客户端,我用它连接到运行带有自签名证书的HTTPS的Jetty 9.3.6服务器(改编自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' } 

希望这可以帮助。

好吧,我尝试使用你的代码来复制问题但没有用(获取证书,使用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 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); } }