无法将node.js中的socket.io连接到tls

我已经googlesearch和stackoverflow,任何线程相关的socket.io + tls,或node.js + tls,或ssl,等等…但我找不到任何方式build立我所需要的:

通过socket.io连接到客户端并使用tls的node.js服务器(仅需要服务器身份validation)

到目前为止,我已经连接服务器和客户端,而不使用tls。

当我创build我的node.js服务器(在端口3000监听),并使用tls我可以使用命令:

openssl s_client -connect localhost:3000 

和everthing似乎是好的,但去了我试图连接我的socket.io客户端我总是有一个错误。

这是我简单的服务器代码:

  var tls = require('tls'); var fs = require('fs'); var options = { key: fs.readFileSync('evServer-key.pem'), cert: fs.readFileSync('evServer-cert.pem'), requestCert: false, }; var server = tls.createServer(options, function(cleartextStream) { console.log('server connected', cleartextStream.authorized ? 'authorized' : 'unauthorized'); cleartextStream.write("welcome!\n"); cleartextStream.setEncoding('utf8'); cleartextStream.pipe(cleartextStream); }); io = require('socket.io').listen(server); server.listen(3000, function() { console.log('server bound'); }); io.sockets.on('connection', function (socket) { // When new users join socket.on('join', function (data, fn) { console.log("User connected"); socket.set('socketname', 'name', function () { socket.emit('Connection received') ; // for the current socket }); }); // --------------- client events socket.on('clientjoin', function (data) { client_socket = socket; console.log("BT Client joined"); }); socket.on('hello', function (data) { client_socket = socket; console.log("Hello received"); socket.emit('Hi there!'); }); // When a client disconnects socket.on('disconnect', function () { socket.get('nickname', function (err, nickname) { console.log(nickname + " logged out") }); }); }); 

这是客户端的连接:

 socket = new SocketIO("http://"+this.serverIpAddress+":3000/"); 

如果我使用:

 http://localhost:3000 

我得到一个连接拒绝错误:

 02-22 15:37:41.702: W/System.err(621): io.socket.SocketIOException: Error while handshaking 02-22 15:37:41.702: W/System.err(621): at io.socket.IOConnection.handshake(IOConnection.java:322) 02-22 15:37:41.702: W/System.err(621): at io.socket.IOConnection.access$600(IOConnection.java:39) 02-22 15:37:41.702: W/System.err(621): at io.socket.IOConnection$ConnectThread.run(IOConnection.java:199) 02-22 15:37:41.702: W/System.err(621): Caused by: java.net.ConnectException: localhost/127.0.0.1:3000 - Connection refused 02-22 15:37:41.702: W/System.err(621): at org.apache.harmony.luni.net.PlainSocketImpl.connect(PlainSocketImpl.java:207) 02-22 15:37:41.712: W/System.err(621): at org.apache.harmony.luni.net.PlainSocketImpl.connect(PlainSocketImpl.java:437) 02-22 15:37:41.712: W/System.err(621): at java.net.Socket.connect(Socket.java:983) 02-22 15:37:41.712: W/System.err(621): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.<init>(HttpConnection.java:75) 02-22 15:37:41.712: W/System.err(621): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.<init>(HttpConnection.java:48) 02-22 15:37:41.712: W/System.err(621): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection$Address.connect(HttpConnection.java:322) 02-22 15:37:41.712: W/System.err(621): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnectionPool.get(HttpConnectionPool.java:89) 02-22 15:37:41.712: W/System.err(621): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getHttpConnection(HttpURLConnectionImpl.java:285) 02-22 15:37:41.712: W/System.err(621): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.makeConnection(HttpURLConnectionImpl.java:267) 02-22 15:37:41.712: W/System.err(621): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.retrieveResponse(HttpURLConnectionImpl.java:1018) 02-22 15:37:41.712: W/System.err(621): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:512) 02-22 15:37:41.742: W/System.err(621): at io.socket.IOConnection.handshake(IOConnection.java:313) 02-22 15:37:41.742: W/System.err(621): ... 2 more 02-22 15:37:41.742: I/io.socket(621): Cleanup 02-22 15:55:57.352: I/dalvikvm(621): Jit: resizing JitTable from 512 to 1024 02-22 16:00:54.763: I/AsyncTask(621): onPreExecute 02-22 16:00:54.772: I/ClientActivity(621): C: Connecting... 02-22 16:00:54.782: I/AsyncTask(621): onPostExecute: Completed. 02-22 16:00:54.792: I/AsyncTask(621): an Error occured 02-22 16:00:54.792: W/System.err(621): io.socket.SocketIOException: Error while handshaking . . . 

如果我使用

 https://localhost:3000 

我得到这个其他的空指针错误:

 02-22 16:06:06.432: I/AsyncTask(666): onPreExecute 02-22 16:06:06.442: I/ClientActivity(666): C: Connecting... 02-22 16:06:06.503: I/AsyncTask(666): onPostExecute: Completed. 02-22 16:06:06.512: I/AsyncTask(666): an Error occured 02-22 16:06:06.512: W/System.err(666): io.socket.SocketIOException: Error while handshaking 02-22 16:06:06.512: W/System.err(666): at io.socket.IOConnection.handshake(IOConnection.java:322) 02-22 16:06:06.512: W/System.err(666): at io.socket.IOConnection.access$600(IOConnection.java:39) 02-22 16:06:06.512: W/System.err(666): at io.socket.IOConnection$ConnectThread.run(IOConnection.java:199) 02-22 16:06:06.523: W/System.err(666): Caused by: java.lang.NullPointerException 02-22 16:06:06.523: W/System.err(666): at io.socket.IOConnection.handshake(IOConnection.java:301) 02-22 16:06:06.523: W/System.err(666): ... 2 more 

我也尝试写我的外部IP地址,而不是本地或127.0.0.1,但它仍然无法正常工作,是不是我想要做的? 任何想法或build议?

先谢谢你。


更新:

我一直在努力,但我没有实现连接客户端和服务器。 我已经在客户端(Android)尝试过,因为我正在使用Gottox的socket.io库,我无法使用user568109提出的解决scheme是将此添加到我的客户端的代码:

  CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream caInput = new BufferedInputStream(new FileInputStream("/sdcard/cacert.crt")); Certificate ca; try { ca = cf.generateCertificate(caInput); } finally { caInput.close(); } // Create a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); SocketIO.setDefaultSSLSocketFactory(SSLContext.getInstance("SSL")); socket = new SocketIO("https://"+this.serverIpAddress+":3000/"); 

其中,cacert.crt是我创build的证书颁发机构的证书,而且我用来签署服务器的密钥。

我可以通过https连接到我的服务器的Web浏览器没有问题,但我的Android应用程序抛出以下错误:

 02-26 16:39:18.420: I/AsyncTask(27913): onPreExecute 02-26 16:39:18.420: I/ClientActivity(27913): C: Connecting... 02-26 16:39:18.445: E/ClientActivity(27913): C: Error 02-26 16:39:18.445: E/ClientActivity(27913): java.security.cert.CertificateException: org.apache.harmony.security.asn1.ASN1Exception: ASN.1 sequence identifier expected at [0], got 43 02-26 16:39:18.445: E/ClientActivity(27913): at org.apache.harmony.security.provider.cert.X509CertImpl.<init>(X509CertImpl.java:106) 02-26 16:39:18.445: E/ClientActivity(27913): at org.apache.harmony.security.provider.cert.X509CertFactoryImpl.getCertificate(X509CertFactoryImpl.java:656) 02-26 16:39:18.445: E/ClientActivity(27913): at org.apache.harmony.security.provider.cert.X509CertFactoryImpl.engineGenerateCertificate(X509CertFactoryImpl.java:109) 02-26 16:39:18.445: E/ClientActivity(27913): at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:195) 02-26 16:39:18.445: E/ClientActivity(27913): at com.android.locaalton.CommTask.doInBackground(CommTask.java:61) 02-26 16:39:18.445: E/ClientActivity(27913): at com.android.locaalton.CommTask.doInBackground(CommTask.java:1) 02-26 16:39:18.445: E/ClientActivity(27913): at android.os.AsyncTask$2.call(AsyncTask.java:287) 02-26 16:39:18.445: E/ClientActivity(27913): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) 02-26 16:39:18.445: E/ClientActivity(27913): at java.util.concurrent.FutureTask.run(FutureTask.java:137) 02-26 16:39:18.445: E/ClientActivity(27913): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230) 02-26 16:39:18.445: E/ClientActivity(27913): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 02-26 16:39:18.445: E/ClientActivity(27913): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 02-26 16:39:18.445: E/ClientActivity(27913): at java.lang.Thread.run(Thread.java:856) 02-26 16:39:18.445: E/ClientActivity(27913): Caused by: org.apache.harmony.security.asn1.ASN1Exception: ASN.1 sequence identifier expected at [0], got 43 02-26 16:39:18.445: E/ClientActivity(27913): at org.apache.harmony.security.asn1.BerInputStream.expected(BerInputStream.java:464) 02-26 16:39:18.445: E/ClientActivity(27913): at org.apache.harmony.security.asn1.BerInputStream.readSequence(BerInputStream.java:502) 02-26 16:39:18.445: E/ClientActivity(27913): at org.apache.harmony.security.asn1.DerInputStream.readSequence(DerInputStream.java:105) 02-26 16:39:18.445: E/ClientActivity(27913): at org.apache.harmony.security.asn1.ASN1Sequence.decode(ASN1Sequence.java:40) 02-26 16:39:18.445: E/ClientActivity(27913): at org.apache.harmony.security.asn1.ASN1Type.decode(ASN1Type.java:91) 02-26 16:39:18.445: E/ClientActivity(27913): at org.apache.harmony.security.provider.cert.X509CertImpl.<init>(X509CertImpl.java:101) 02-26 16:39:18.445: E/ClientActivity(27913): ... 12 more 

任何关于这个新问题的想法?

Web套接字可以是未encryption的ws://或encryption的wss://(ws + ssl)。 Socket.io允许使用这样的安全选项在两者之间进行select:

 <script src="http://localhost:3000/socket.io/socket.io.js"></script> socket = io.connect('http://localhost:3000', {secure:true}); //encrypted socket = io.connect('http://localhost:3000', {secure:false}); //unencrypted 

这应该做你所有的websocketencryption要求。 无论您遇到什么错误,都是因为您正在尝试将您的tls服务器用作http / https服务器。 openssl s_client -connect localhost:3000是访问它的正确方法。 http://localhost:3000将需要在该地址运行http服务器,但您的服务器是tls( http://nodejs.org/api/tls.html#tls_tls_ssl )。

你应该使用它作为https服务器。 它是tls.Server的子类,并发出与http.Server相同的事件。 http://nodejs.org/api/https.html#https_class_https_server

 var https = require('https'); //tls changed to https var server = https.createServer(options, function(cleartextStream) { console.log('server connected', 

你的服务器现在是https,你的https://localhost:3000应该可以工作。 尝试使用openssl s_client连接到它来检查它是否工作。

更新后的评论

user1249517您应该在提问时指定错误的来源。 你在node.js,ssl和socket.io下标记它,但它应该是java,android和socket。 socket.io是JavaScript库,而你正在使用gottox Java库socket.io。

无论如何,当你使用浏览器来查看网站时,证书由浏览器自己pipe理,当你使用java应用程序时,它应该处理证书。 你得到的错误:

 Caused by: org.apache.harmony.security.asn1.ASN1Exception: ASN.1 sequence identifier expected at [0], got 43 

ASN.1是X.509证书的编码格式。 ASN.1 sequence identifier expected将意味着您的证书是损坏/无效的ASN.1格式。 您不应该直接使用您的证书,而是使用openssl将其转换为ASN.1格式。 尝试这样的事情。

 openssl asn1parse -in mycert.pem 

指定authentication的input格式并在parsing之前对其进行validation。 如果所有证书都是有效的,那么在代码中有一些错误。

我更新的解决scheme是创build一个证书颁发机构,使用它来签署服务器的证书,并在Android手机中安装证书。

解决之后,我不得不面对新的问题,那些在这里对待:

https://github.com/Gottox/socket.io-java-client/issues/14

解决scheme也在链接里面

希望这可以帮助别人!