Skip to content

dart:io WebSocket client cannot connect to WebSocket server with pinned, self-signed certificate #34284

Open
@CryptUser

Description

@CryptUser

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:

var securityContext = SecurityContext();
var bytesList = utf8.encode(cert);
var bytes = bytesList is Uint8List ? bytesList : Uint8List.fromList(bytesList);
securityContext.setTrustedCertificatesBytes(bytes);
SecureSocket secureSocket = await SecureSocket.connect('192.168.0.11', 9000, context: securityContext);
socket = WebSocket.fromUpgradedSocket(secureSocket);

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)?

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2A bug or feature request we're likely to work onarea-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.library-iotype-enhancementA request for a change that isn't a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions