5

I wrote a wss server in Nodejs and now I'm trying to connect to such server using Flutter.

Here's the code in NodeJS:

//Dependencies
const WebSocket = require('ws');
const fs = require('fs');
const https = require('https');
//Dependencies

//Server declarations
const server = https.createServer({
    key: fs.readFileSync('pathTo/key.pem'),
    cert: fs.readFileSync('pathTo/cert.pem')
});
server.listen(xxxx);
const wss = new WebSocket.Server({ server });
//Server declarations

wss.on('connection', function connection(ws)
{
  ws.on('message', function incoming(message)
  {
      console.log('Received: ' + message);
      ws.send('echo: ' + message);
   });
   ws.send('Connected!');
});

Here's the code in Flutter:

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:web_socket_channel/io.dart';
import 'package:connectivity/connectivity.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

class MyApp extends StatelessWidget
{
  @override
  Widget build(BuildContext context)
  {
    final title = 'LumenApp Prototype';
    IOWebSocketChannel channel;
    try
    {
      channel = new IOWebSocketChannel.connect('wss://xxxxxxxx.xxx.xxx:xxxx/');
      MyHomePageState.noResponse = false;
    }
    catch(e)
    {
      MyHomePageState.noResponse = true;
    }
    return MaterialApp(
      title: title,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        primaryTextTheme: TextTheme(
          title: TextStyle(
            color: Colors.yellow[600],
          ),
        ),
      ),
      home: MyHomePage(
        title: title,
        channel: channel,
      ),
    );
  }
}

The error on Flutter is: WebSocketChannelException: WebSocketChannelException: HandshakeException: Handshake error in client (OS Error: CERTIFICATE_VERIFY_FAILED: self signed certificate(handshake.cc:354))

This happens inside this function:

  void initPlatformState()
  {
    widget.channel.stream.listen((message)
    {
      setState(() { noResponse = false; });
      //Handle message...
    },
    onError: (error)
    {
      print(error);
      if(mounted)
      {
        setState((){ noResponse = true;});
      }
    },
    onDone: ()
    {
      if(mounted)
      {
        setState((){ noResponse = true; });
      }
    });
  }

I used a self-signed certificate server-side made with openssl.
Any idea how to solve this?

CrystalSpider
  • 377
  • 7
  • 16
  • The first assumption would be that your websocket server doesn't have an SSL certificate, and as such cannot run WSS. – J. S. Jan 03 '20 at 15:14
  • @JoãoSoares I got an ssl key and an ssl certificate using OpenSSL. – CrystalSpider Jan 03 '20 at 15:18
  • Have you tested that your WSS is working properly on a different client that isn't your Flutter app? – J. S. Jan 03 '20 at 15:21
  • @JoãoSoares I tried with Postman, but it doesn't work either way, neither with ws nor with wss, even when with ws and Flutter it works. – CrystalSpider Jan 03 '20 at 15:34
  • There's definitely something odd going on there, but you need to make sure your server is working properly before moving on to seeing if there's a problem on your Flutter app. – J. S. Jan 03 '20 at 15:36
  • @JoãoSoares okay, I tried with the web socket testing google chrome extension and it works just fine with ws. With wss it tries to open the channel for a little while and then it prompts "error". I guess the problem is on server side, but I can't tell what it is. – CrystalSpider Jan 03 '20 at 15:40
  • My knowledge of NodeJS WebSockets is limited, but strangely I can't find the SSL settings for the [`ws`](https://github.com/websockets/ws/blob/HEAD/doc/ws.md#class-websocketserver) NodeJS package for WebSockets. Maybe this might help you: https://stackoverflow.com/questions/35190045/websocket-ws-ssl – J. S. Jan 03 '20 at 15:48
  • @JoãoSoares I edited the post with new information. – CrystalSpider Jan 03 '20 at 16:13
  • Can you please share your Flutter code and which package you are using as a WebSocket client? – J. S. Jan 03 '20 at 16:15
  • @JoãoSoares sure, done. – CrystalSpider Jan 03 '20 at 16:20
  • It doesn't seem like there's any issue on the Flutter side. I wish I could help you more on the NodeJS side, but it's just outside my area of knowledge. I can only suggest trying to connect to a public WebSocket API with your Flutter App to test if it is working correctly. – J. S. Jan 03 '20 at 16:44
  • @JoãoSoares yes it does. Btw I found the error, so I'll edit my question with it. – CrystalSpider Jan 03 '20 at 16:45
  • That's great. I'm sorry I couldn't help you more. – J. S. Jan 03 '20 at 16:49
  • @JoãoSoares do not worry, I found the error because of your comments. – CrystalSpider Jan 03 '20 at 16:52
  • I'm glad. Good luck with your app! – J. S. Jan 03 '20 at 16:59
  • @JoãoSoares thanks. – CrystalSpider Jan 03 '20 at 17:00

1 Answers1

1

If you happen to bumped in this GitHub post, you can follow the temporary fix from this comment:

class MyHttpOverrides extends HttpOverrides{
  @override
  HttpClient createHttpClient(SecurityContext context){
    return super.createHttpClient(context)
      ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
  }
}

void main(){
    HttpOverrides.global = new MyHttpOverrides();
    runApp(new MyApp());
}

It works on local ip with self signed certificate.

To elaborate, here is the same solution:

Just for the sake of clarity specially for the newcomers to Flutter/Dart, here is what you need to do in order to enable this option globally in your project:

  1. In your main.dart file, add or import the following class:
HttpClient createHttpClient(SecurityContext? context){
    return super.createHttpClient(context)
      ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;   } } ```
  1. In your main function, add the following line after function definition:

HttpOverrides.global = MyHttpOverrides();

This comment was very helpful to pass through this matter, and please note that...

This should be used while in development mode, do NOT do this when you want to release to production, the aim of this answer is to make the development a bit easier for you, for production, you need to fix your certificate issue and use it properly, look at the other answers for this as it might be helpful for your case.

Another thing worth mentioning, signed certificates are available for free now (https://letsencrypt.org/).

Also, I think the Flutter team is working to enhance the documentation for better reference regarding this issue. It is being tracked here.

MαπμQμαπkγVπ.0
  • 5,887
  • 1
  • 27
  • 65