godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.14k stars 21.19k forks source link

mbedtls error when using private key on server #37739

Closed creikey closed 3 years ago

creikey commented 4 years ago

Godot version: Godot Engine v3.2.1.stable.official - https://godotengine.org , server build OS/device including version: Ubuntu 18.04.4 LTS Issue description: Console prints mbedtls error: returned -0x7780 and connection is not established Steps to reproduce: Using the server build on linux try to use a private key from LetsEncrypt , then have a godot client connect. Minimal reproduction project:

extends Node

var server: WebSocketServer = null

func _ready():
    server = WebSocketServer.new()
    server.private_key = load("res://privkey.key")
    server.ssl_certificate = load("res://fullchain.crt")
    server.listen(port, PoolStringArray(), true)
    get_tree().set_network_peer(server)

func _process(delta):
    if server.is_listening():
        server.poll()
creikey commented 4 years ago

Based on this issue thread: https://tls.mbed.org/discussions/crypto-and-ssl/mbedtls_x509_crt_parse-of-client-certificate-return-error-4480 It seems to be a platform specific error in how Godot is using the library

creikey commented 4 years ago

I was able to get more error messages when using the headless build instead of the server build:


thirdparty/mbedtls/library/ssl_tls.c:4369: mbedtls_ssl_handle_message_type() returned -30592 (-0x7780)
thirdparty/mbedtls/library/ssl_srv.c:3734: mbedtls_ssl_read_record() returned -30592 (-0x7780)
thirdparty/mbedtls/library/ssl_tls.c:8315: mbedtls_ssl_handshake() returned -30592 (-0x7780)
mbedtls error: returned -0x7780```
creikey commented 4 years ago

To clarify I have checked the privkey using openssl as the forum post suggested and all looks well

creikey commented 4 years ago

The keys I was using were out of date.... apologies

Demindiro commented 3 years ago

I have the exactly the same issue but with an up-to-date certificate I got from Let's Encrypt. HTML5 exports are able to connect to the server successfully but standalone clients fail.

Reproduction project (using self-signed certificate): mbedtls-standalone.zip

Output:

thirdparty/mbedtls/library/ssl_tls.c:5865: x509_verify_cert() returned -9984 (-0x2700)
ERROR: _do_handshake: TLS handshake error: -9984
   At: modules/mbedtls/stream_peer_mbedtls.cpp:86.
mbedtls error: returned -0x2700

Client failed to connect
SCRIPT ERROR: _client_error: Assertion failed.
   At: res://main.gd:33.
thirdparty/mbedtls/library/ssl_tls.c:5286: is a fatal alert message (msg 42)
thirdparty/mbedtls/library/ssl_tls.c:4477: mbedtls_ssl_handle_message_type() returned -30592 (-0x7780)
thirdparty/mbedtls/library/ssl_srv.c:3759: mbedtls_ssl_read_record() returned -30592 (-0x7780)
thirdparty/mbedtls/library/ssl_tls.c:8425: mbedtls_ssl_handshake() returned -30592 (-0x7780)
mbedtls error: returned -0x7780
Demindiro commented 3 years ago

So apparently the issue was that I mistakenly used cert.pem instead of fullchain.pem. Using the latter fixes the issue.

The error is very vague and I feel like this should at least be documented somewhere.

Calinou commented 3 years ago

cc @Faless

Faless commented 3 years ago

In general, SSL is based on a chain of trust. The whole chain must be trusted for the certificate to be trusted. Browsers comes with quite a few trusted top-level certificates, so you don't always need the full chain in browsers. Tools that checks SSL validity also easily spot and report the fact that the full chain is missing (e.g. https://www.digicert.com/help/ or https://www.sslshopper.com/ssl-checker.html)

creikey commented 3 years ago

In general, SSL is based on a chain of trust. The whole chain must be trusted for the certificate to be trusted. Browsers comes with quite a few trusted top-level certificates, so you don't always need the full chain in browsers. Tools that checks SSL validity also easily spot and report the fact that the full chain is missing (e.g. https://www.digicert.com/help/ or https://www.sslshopper.com/ssl-checker.html)

Those links should be on the websockets doc page https://docs.godotengine.org/en/stable/tutorials/networking/websocket.html

lumenwrites commented 3 years ago

I'm getting the same error.

I have used letsencrypt to generate my keys:

Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/mydomain.io/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/mydomain.io/privkey.pem

I am converting .pem files into certificate.crt and private.key files like so:

openssl x509 -outform der -in /etc/letsencrypt/live/mydomain.io/fullchain.pem -out certificate.crt
openssl rsa -outform der -in /etc/letsencrypt/live/mydomain.io/privkey.pem -out private.key

To start a server, I'm doing:

var net = WebSocketServer.new()
net.private_key = load("res://HTTPSKeys/private.key")
net.ssl_certificate = load("res://HTTPSKeys/certificate.crt")
net.listen(PORT, PoolStringArray(), true)

On client, I'm trying to connect to the server like so:

net = WebSocketClient.new()
var error = net.connect_to_url('wss://178.62.117.12:6969', PoolStringArray(), true)

When I'm trying to run the game in browser, I'm getting:

WebSocket connection to 'wss://178.62.117.12:6969/' failed: 
Error in connection establishment: net::ERR_CERT_COMMON_NAME_INVALID

And on the server I'm getting:

mbedtls error: returned -0x7780

The complete code for the project is here.

Can someone please help me to figure out what I'm doing wrong?

Faless commented 3 years ago

var error = net.connect_to_url('wss://178.62.117.12:6969', PoolStringArray(), true)

@lumenwrites Well, you need to put the fully qualified domain name there, not the IP address. The browser is also telling you that: ERR_CERT_COMMON_NAME_INVALID

lumenwrites commented 3 years ago

@Faless Oh my god, thank you SO much!!! It works now! AMAZING. I'm so happy!!

Faless commented 3 years ago

Closed via #47708

tavurth commented 3 years ago

Is there any way to run wss:// every time across a LAN?

This would be useful when using godot for IoT and other local apps. See this stack overflow question for more information of use-cases.

For me around 50% of the times this is working just fine, and the other 50% it's failing with mbedtls error: returned -0x6c00. I had to track down this bug report from google as the error is still confusing on 3.3.2

If it shouldn't work I'm confused about the 50% of the time it seems to work 🤔 Perhaps we should display a friendly error message like: "wss is only supported with a qualified domain name"

ZhangWei-bi commented 11 months ago

Is there any way to run wss:// every time across a LAN?

This would be useful when using godot for IoT and other local apps. See this stack overflow question for more information of use-cases.

For me around 50% of the times this is working just fine, and the other 50% it's failing with mbedtls error: returned -0x6c00. I had to track down this bug report from google as the error is still confusing on 3.3.2

If it shouldn't work I'm confused about the 50% of the time it seems to work 🤔 Perhaps we should display a friendly error message like: "wss is only supported with a qualified domain name"

@tavurth I have a client, server1 and server2. Server1 is open to the world, using let's encrypt certs for the client. Server2 is only opened to server1. When a client connects to server1, server1 connects to server2 with a self signed cert (think this is where the issue is). I'm on Godot 4.0.4 and I am getting this exact error. It only happens once in a while, but once it happens, no one can connect until I restart server1. I'm also using an ip instead of a url since I don't have a domain name for that server. Did you ever figure out this issue? I'm not using websocket but I am doing something like this:

// This part is for the server and client, it should be fine.
//server1
        var m_net = ENetMultiplayerPeer.new()
    m_net.create_server(port, 4000)
    m_publicKey = load_pem_file("/home/username/fullchain_copy.pem")
    var publicKey = X509Certificate.new()
    var error = publicKey.load("/home/username/fullchain_copy.pem")
    var privateKeyPem = load_pem_file("/home/username/privkey_copy.pem")
    var privateKey = CryptoKey.new()
    privateKey.load_from_string(privateKeyPem)
    m_net.host.dtls_server_setup(TLSOptions.server(privateKey, publicKey))

// client
    m_net = ENetMultiplayerPeer.new()
    m_net.create_client("urlHere", xxx)
    m_net.host.dtls_client_setup("urlHere", TLSOptions.client())
    multiplayer.set_multiplayer_peer(m_net)
//This is where the issue is I think when server1 connects to server2

// server1 (connects to server2):
    m_net = ENetMultiplayerPeer.new()
    m_net.create_client("xxx.xxx.xxx.xxx", xxxx)
    var publicKey = X509Certificate.new()
    publicKey.load("/home/username/publicKey.pem")
    m_net.host.dtls_client_setup("serverName", TLSOptions.client_unsafe(publicKey))

//server2:
        var m_net = ENetMultiplayerPeer.new()
    m_net.create_server(xxxx, 4000)
        var publicKey = X509Certificate.new()
    publicKey.load("/home/username/publicKey.pem")
    var privateKey = CryptoKey.new()
    var privateKeyPem = load_pem_file("/home/username/privateKey.pem")
    privateKey.load_from_string(privateKeyPem)
    m_net.host.dtls_server_setup(TLSOptions.server(privateKey, publicKey))
    multiplayer.set_multiplayer_peer(m_net)
func load_pem_file(file_path: String) -> String:
    var file = FileAccess.open(file_path, FileAccess.READ)
    if file:
        var pem_content : String = file.get_as_text()
        file.close()
        return pem_content
    else:
        print("Failed to open PEM file.")
        return ""

If anyone else has any ideas, let me know.