Starscream: When connecting to a wss:// socket, getting an SSL handshake error

The same host with a different WebSock Swift framework (https://github.com/tidwall/SwiftWebSocket) connects just fine. Am I missing some extra configuration?

Error printout:

[BoringSSL] boringssl_context_handle_fatal_alert(1872) [C1.1:1][0x7fcec6701630] write alert, level: fatal, description: certificate unknown
[BoringSSL] boringssl_context_error_print(1862) boringssl ctx 0x6000000d40a0: 140526066114904:error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED:/BuildRoot/Library/Caches/com.apple.xbs/Sources/boringssl_Sim/boringssl-283.60.3/ssl/handshake.cc:369:
[BoringSSL] boringssl_session_handshake_error_print(111) [C1.1:1][0x7fcec6701630] 140526066114904:error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED:/BuildRoot/Library/Caches/com.apple.xbs/Sources/boringssl_Sim/boringssl-283.60.3/ssl/handshake.cc:369:
[BoringSSL] nw_protocol_boringssl_handshake_negotiate_proceed(726) [C1.1:1][0x7fcec6701630] handshake failed at state 12288
error: Optional(-9858: Optional(handshake failed))

Code that triggers the SSL handshake error:

websock = WebSocket(request: URLRequest(url: URL(string: "wss://myhost:9110/")!))
websock.delegate = self
websock.connect()

    func didReceive(event: WebSocketEvent, client: WebSocket) {
        switch event {
        case .connected(let headers):
            print("websocket is connected: \(headers)")
        case .disconnected(let reason, let code):
            print("websocket is disconnected: \(reason) with code: \(code)")
        case .text(let string):
            print("Received text: \(string)")
        case .binary(let data):
            print("Received data: \(data.count)")
        case .ping(_):
            break
        case .pong(_):
            break
        case .viablityChanged(_):
            break
        case .reconnectSuggested(_):
            break
        case .cancelled:
            print("cancelled")
        case .error(let error):
            print("error: \(String(describing: error))")
        }
    }

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 11
  • Comments: 16 (1 by maintainers)

Most upvoted comments

Seems like v4.0.0 has a default value set for certificate pinning when initialising WebSocket, so it always tries to pin a certificate by default, even if you don’t have one.

public convenience init(request: URLRequest, certPinner: CertificatePinning? = FoundationSecurity(), compressionHandler: CompressionHandler? = nil)

Passing nil to certPinner: disables it and allows you to successfully make a connection. Like so:

WebSocket(request: urlRequest, certPinner: nil)

Passing nil to certPinner will not work as it will generate FoundationSecurity object which has allowSelfSigned is set to false by default. Here is how I did it

WebSocket(request: encodedRequest, certPinner: FoundationSecurity(allowSelfSigned: true))

Same issue here. I downgraded to 3.1.1 and the problem is gone, but I can reproduce it on 4.0.0, so I tried to figure out what was the difference between versions. The git-diff is quite big, so I did not check all the changes in sourcese, but I’ve noticed this commit https://github.com/daltoniam/Starscream/commit/a986e38e563af99a6b0149ba20162692cb1aecec, so I went to excon’s changelog to figure out if there was a certificate/TLS-related change there and noticed:

0.70.0 2019-12-02
=================

Update bundled certificates

This might be somehow related to this issue, but I’m not 100% sure about that (the rest of the changes between 3.1.1 and 4.0.0 need to be checked as well).

Facing same issue myself.