-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
dart:io WebSocket client cannot connect to WebSocket server with pinned, self-signed certificate #34284
Comments
I got a step further after finding the onBadCertificate parameter. Using: SecureSocket secureSocket = await SecureSocket.connect('192.168.0.11', 9000, I did no longer get an error. However, even after the line socket = WebSocket.fromUpgradedSocket(secureSocket, serverSide: false); I did not get any web socket connection. The server-log showed after some seconds: dropping connection to peer tcp4:192.168.0.11:51735 with abort=True: WebSocket opening handshake timeout (peer did not finish the opening handshake in time) Note that the web socket server is written in Python and running well with native Android code. In case it is not possible to use the WebSocket.fromUpgradedSocket(...) constructor with non-dart servers, I would suggest to add the onBadCertificate parameter directly to the Websocket.connect(...) constructor. |
Similar to #31948 |
@zoechi Is that hint only aimed at command line apps? Since I'm running flutter, I tried with Running the app when using the constructor Running with the WebSocket.fromUpgradedSocket(...) constructor gave the same result as described above. |
What other kind of apps do you have in mind? |
I am trying to implement an Android/ios app with flutter. As pointed out above, my idea was to use a SecureSocket which would allow a self-signed certificate, and then use the WebSocket.fromUpgradedSocket(...) constructor. Which I however did not get to work, as shown above. If this strategy does not work, my question is, wouldn't it be best if the WebSocket.connect constructor would also get the additional parameter "onBadCertificate" to allow self-signed and pinned certificates? |
I am in the same boat as @CryptUser The platform I develop for allows users to upload and use self signed certs. It would be very beneficial to have an additional callback or param to allow selfsigned certs. |
Another 'me too' comment, I'm afraid. Also in the same situation as @CryptUser and @linuxjet - I need to open a secure websocket Flutter in our development environment to our development server, and despite importing the certificate into the Android emulator, I see:
The (edit) Something like |
I'm having the same problem. In a nodejs environment it is actually pretty easy var ws = new WebSocket('wss://127.0.0.1:8334/ws', {
headers: {
'Authorization': 'Basic '+new Buffer(user+':'+password).toString('base64')
},
cert: cert,
ca: [cert]
}); |
I also have this problem. Are there any plans to fix this in flutter? |
I do some investigation to a problem. Searching possible workarounds and trying to reimplement some part of dart:io(http) websocket_impl class on my own. This led me to understand the root of the problem: HttpClient initialized statically with default factory constructor without any access to it and ability to control this step: Problems:
If SDK expose this HttpClient to outside, a lot of things can be available for websocket (SSL pinning, etc) one of the straightforward solution will be accept HttpClient like a one of the parameters in
if param is specified - it will be used instead static field.
who thinks what? |
Hi @nailgilaziev |
same problem here. I'm using a self-signed certificate for development and I can't connect to the server. |
I finally gave up and used RawSocket. Something like this:
|
I still have the problem, unfortunately RawSocket does not satisfy my problem, RawSocket runs an nslookup and cannot find the server and this breaks my connection since my socket server url carries a "/ params" parameter and the websocket can handle this problem, but I need the onBadcertificate parameter. |
I see. So just connect the socket to the host and make a connection to the endpoint in socket write. here is one of my connection functions:
Let me know if this helps you out. |
Can you post your websocket with this code ? |
For my case class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext context) {
return super.createHttpClient(context)
..badCertificateCallback =
(X509Certificate cert, String host, int port) => true; // add your localhost detection logic here if you want
}
}
void main() {
HttpOverrides.global = new MyHttpOverrides();
runApp(MyApp());
} |
I created a PR that solves the problem, @sortie anything more I need to do? I have this running on my local machine without an issue. |
After two days struggling with this problem, I gave up and started using WebSocket. It's more low level, but is easy to implement and solved my problem. void startListening() async {
_socket = await WebSocket.connect("wss://localhost");
_socket.listen(
(event) => print('Server: $event'),
onError: (error) => print(error),
onDone: () => print("Done"),
);
await sendMessage('Hello World! 1');
await sendMessage('Hello World! 2');
await sendMessage('Hello World! 3');
}
Future<void> sendMessage(String message) async {
print('Client: $message');
_socket.add(message);
await Future.delayed(Duration(seconds: 2));
}
|
This is a great temporary fix ! it works on local ip with self signed certificate. (Please modify the class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback =
(X509Certificate cert, String host, int port) => true; // add your localhost detection logic here if you want
}
}
void main() {
HttpOverrides.global = MyHttpOverrides();
runApp(MaterialApp(home: MyApp()));
} |
Thanks @lmint1 ! |
… custom HTTP Client for web socket connections. The WebSocket abstract class was changed to allow an optional parameter called customClient that takes in a HTTPClient and passes it to the WebSocket Implementation. The WebSocket implementation takes the customClient, checks if its null, if its not null, it uses the customClient in place of the static HTTPClient that the WebSocket Implementation offers. This custom client does not override the static HTTPClient, so all previous functionality remains the same when the customClient is not present. TEST=testStaticClientUserAgentStaysTheSame() in web_socket_test.dart in standalone_2/standalone TEST=new SecurityConfiguration(secure: true).runTests(); in web_socket_error_test.dart and web_socket_test.dart in standalone_2/standalone Bug: #34284 Closes #46040 #46040 GitOrigin-RevId: 58fed38 Change-Id: I042b1e3fa7a4effed076c0deeec1f86af0dfe26d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/200262 Reviewed-by: Alexander Aprelev <aam@google.com> Reviewed-by: Siva Annamalai <asiva@google.com> Commit-Queue: Alexander Aprelev <aam@google.com>
Just letting you all know, this issue has been fixed, within WebSocket.connect() you can add a custom HTTP Client to it now. |
Is there any Update on this Issue? |
I have a WebSocket server at ip address 192.168.0.11 with external port 9000, and a self-signed certificate.
The server is tested to work ok with an ios client using SocketRocket.
When I try to connect with my flutter app using
socket = await WebSocket.connect('wss://192.168.0.11:9000');
I get the following error message:
[VERBOSE-2:dart_error.cc(16)] Unhandled exception:
HandshakeException: Handshake error in client (OS Error:
CERTIFICATE_VERIFY_FAILED: ok(handshake.cc:363))
#0 _WebsocketPlaygroundHomeState.connectWebsocket (package:quano_flutter/ui/debug/websocket_playground.dart:92:14)
#1 _WebsocketPlaygroundHomeState.build. (package:quano_flutter/ui/debug/websocket_playground.dart:138:19)
#2 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:494:14)
#3 _InkResponseState.build. (package:flutter/src/material/ink_well.dart:549:30)
#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24)
#5 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:161:9)
#6 TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:94:7)
#7 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:315:9)
#8 <…>
Note that I cannot just replace the server certificate with one signed by a CA: The reason I want to use a pinned, self-signed certificate is security. The public part of the certificate will be saved in the app, and only connections to a server with the matching certificate will be allowed, thus preventing man-in-the-middle attacks. Pinning a self-signed certificate has the advantage, that we as app developers do not need to trust any CA.
My idea then was to make the app aware of the certificate using a SecurityContext, but WebSocket.connect(...) does not seem to take a SecurityContext. Having a String "cert" that contains the certificate, I instead tried the following code that makes use of the fromUpgradedSocket constructor:
However, I got the following error:
[VERBOSE-2:dart_error.cc(16)] Unhandled exception:
HandshakeException: Handshake error in client (OS Error:
CERTIFICATE_VERIFY_FAILED: ok(handshake.cc:363))
#0 _SecureFilterImpl.handshake (dart:io/runtime/binsecure_socket_patch.dart:96:51)
#1 _RawSecureSocket._secureHandshake (dart:io/secure_socket.dart:779:21)
#2 _RawSecureSocket._tryFilter. (dart:io/secure_socket.dart:900:13)
#3 _RootZone.runUnary (dart:async/zone.dart:1381:54)
#4 _FutureListener.handleValue (dart:async/future_impl.dart:129:18)
#5 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:633:45)
#6 Future._propagateToListeners (dart:async/future_impl.dart:662:32)
#7 Future._completeWithValue (dart:async/future_impl.dart:477:5)
#8 Future._asyncComplete. (dart:async/future_impl.dart:507:7)
#9 _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
#10 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
My version details are:
Dart VM version: 2.0.0-dev.66.0 (Fri Jun 29 11:19:05 2018 +0200) on "macos_x64"
The question is, how to connect to a WebSocket server with a self-signed certificate, and how to only allow the connection if the certificate matches the one saved in the app (certificate pinning)?
The text was updated successfully, but these errors were encountered: