【问题标题】:Unable to connect socket.io in node.js with tls无法使用 tls 连接 node.js 中的 socket.io
【发布时间】:2013-02-08 04:44:51
【问题描述】:

我已经在 stackoverflow 中搜索并搜索了任何与 socket.io+tls 或 node.js+tls 或 ssl 等相关的线程......但我找不到任何构建我需要的方法:

一个node.js服务器通过socket.io连接到客户端,使用tls(只需要服务器认证)

到目前为止,我已经在不使用 tls 的情况下连接了服务器和客户端。

当我创建我的 node.js 服务器(侦听端口 3000)并使用 tls 时,我可以使用以下命令:

openssl s_client -connect localhost:3000

一切似乎都还好,但我尝试连接我的 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 地址而不是 localhost 或 127.0.0.1 但它仍然不起作用,我想做什么是不可能的吗?有什么想法或建议吗?

提前谢谢你。


更新:

我一直在努力,但我还没有实现连接客户端和服务器。 我在客户端(android)中尝试过的,因为我使用的是 Gottox 的 socket.io 库并且我无法使用 user568109 提出的解决方案,所以将其添加到我的客户端代码中:

        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 是我创建的证书颁发机构的证书,我用它来签署服务器的密钥。

我可以毫无问题地通过 https 将网络浏览器连接到我的服务器,但我的 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

对这个新问题有任何想法吗?

【问题讨论】:

    标签: node.js ssl socket.io


    【解决方案1】:

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

    <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 
    

    这应该满足您所有的 websocket 加密要求。无论您遇到什么错误,都是因为您试图将 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 库,而您正在为 socket.io 使用 gottox java 库。

    无论如何,当您使用浏览器查看网站时,证书由浏览器自己管理,当您使用 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
    

    指定证书的输入格式并在解析前对其进行验证。如果所有证书都有效,则代码中存在一些错误。

    【讨论】:

    • 哇!感谢您的回答和您的时间,它看起来真的很有希望。一旦我尝试您的建议,我将发布结果。很清楚的解释!
    • 我已经尝试过 https 服务器,正如你所说,它与 openssl s_client 完美配合,但我之前没有意识到我在客户端中使用 java socket.io(Gottox socket.io over android ) 然后,我不能像你建议的那样创建套接字 ({secure:true}),我会在星期一完成它。
    【解决方案2】:

    我更新的解决方案是创建一个证书颁发机构,用它来签署服务器的证书,并将证书安装在android手机中。

    解决了我不得不面对的新问题后,这里处理的问题:

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

    解决方案也在链接内

    希望这对某人有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-30
      • 2018-05-14
      相关资源
      最近更新 更多