ninenines/cowboy

when use cowboy:start_tls can not connect

kedimomo opened this issue · 7 comments

when i change start_clear to start_tls , can not connet

        ssl_opts() ->
            [
                {port, 8443},
                {certfile, "certfile"},
                {keyfile, "keyfile"}
            ].

         Opts = ssl_opts(), 
	cowboy:start_tls(
		http, 
		Opts, 
		#{env => #{dispatch => Dispatch}}
	).
// 为客户端1创建一个WebSocket连接
var ws1 = new WebSocket('ws://localhost:8080/ws');

// 为客户端2创建一个WebSocket连接
var ws2 = new WebSocket('ws://localhost:8080/ws');

// 创建一个函数来发送消息
function sendMessage(ws, function_name, args) {
    ws.send(JSON.stringify({
        function: function_name,
        args: args
    }));
}

// 连接成功后的回调函数
ws1.onopen = function () {
    console.log('客户端1连接成功');
    // 客户端1注册
    sendMessage(ws1, 'register_client', ['client1']);
};
ws2.onopen = function () {
    console.log('客户端2连接成功');
    // 客户端2注册
    sendMessage(ws2, 'register_client', ['client2']);
};

// 从服务器接收到信息时的回调函数
ws1.onmessage = function (event) {
    console.log('客户端1收到服务器响应', event.data);
};

ws2.onmessage = function (event) {
    console.log('客户端2收到服务器响应', event.data);
};

// 连接失败后的回调函数
ws1.onerror = function (event) {
    console.log("客户端1连接失败了");
};

ws2.onerror = function (event) {
    console.log("客户端2连接失败了");
};

ws1.onclose = function (event) {
    console.log('连接关闭1,状态码:' + event.code);
};
ws2.onclose = function (event) {
    console.log('连接关闭2,状态码:' + event.code);
};

chrome show

VM236:48 客户端1连接失败了
VM236:56 连接关闭1,状态码:1006
VM236:52 客户端2连接失败了
VM236:59 连接关闭2,状态码:1006

keyfile.zip

essen commented

You changed the port in Erlang but not in Javascript?

You changed the port in Erlang but not in Javascript?

thanks , then i change the code , port change to 8443 and ws:// change to wss://

still output : connect error
客户端1连接失败了
客户端2连接失败了


// 为客户端1创建一个WebSocket连接
var ws1 = new WebSocket('wss://localhost:8443/ws');

// 为客户端2创建一个WebSocket连接
var ws2 = new WebSocket('wss://localhost:8443/ws');

// 创建一个函数来发送消息
function sendMessage(ws, function_name, args) {
    ws.send(JSON.stringify({
        function: function_name,
        args: args
    }));
}

// 连接成功后的回调函数
ws1.onopen = function () {
    console.log('客户端1连接成功');
    // 客户端1注册
    sendMessage(ws1, 'register_client', ['client1']);
};
ws2.onopen = function () {
    console.log('客户端2连接成功');
    // 客户端2注册
    sendMessage(ws2, 'register_client', ['client2']);
};

// 从服务器接收到信息时的回调函数
ws1.onmessage = function (event) {
    console.log('客户端1收到服务器响应', event.data);
};

ws2.onmessage = function (event) {
    console.log('客户端2收到服务器响应', event.data);
};

// 连接失败后的回调函数
ws1.onerror = function (event) {
    console.log("客户端1连接失败了");
};

ws2.onerror = function (event) {
    console.log("客户端2连接失败了");
};

ws1.onclose = function (event) {
    console.log('连接关闭1,状态码:' + event.code);
};
ws2.onclose = function (event) {
    console.log('连接关闭2,状态码:' + event.code);
};
essen commented

You probably need to configure TLS options a bit more. In any case I can't really help without more details like the exact error, versions used, browser, etc. But it shouldn't be too difficult to figure out on your own.

You probably need to configure TLS options a bit more. In any case I can't really help without more details like the exact error, versions used, browser, etc. But it shouldn't be too difficult to figure out on your own.

Thank you,. After checking, it was my problem. Because I used rebar3, and then the root directory of the cmd startup rebar3 shell and the src directory of the code are not in the same folder, so the certificate was not actually read. After changing the path, it worked. Erlang is so difficult, especially the prompts in the cmd window, which seem to be garbled and do not have line breaks. Now I am stuck on how to use Erlang to access other https servers. The native httpc has been reporting errors. After checking, I found that there is a ready-made http client framework, gun.

Thinking about it at the beginning, I wanted to use Erlang to chat, but now I am being rubbed on the ground, and I think it's a bit too simple. I thought about it again today. It seems that websocket does not need ssl, it can be directly used with http, let JavaScript use rsa to encrypt the information, and then package it into the JWT format, use cowboy's websocket to send it to the other party, let the other party's JavaScript client decrypt it, end-to-end encryption, this part is completed. But what is a good way for user verification of Erlang cowboy, now everyone can access it. It's so difficult. Now I want to use Erlang gun to access the PHP server, just like JavaScript post login.

It should work the same as any other HTTP client. Depending on the server it may use the authorization header, or login + cookies, or something else. But in all cases except one it's about setting and reading the right HTTP headers. In general tutorials that work for other clients will work for Gun.

The one exception is using TLS certificates for authentication which is probably not what you need.

Note that if using Gun to connect to a TLS server you need a little bit more configuration for it to work with OTP-26 right now. There's a recent ticket about that.

这段就用中文写了,留给有需要的人,如何使用SSL ,除了按照作者的文档,还需要导入证书。因为如果你用浏览器访问websocket的接口,但是自签名证书没有导入浏览器,你是无法访问的,会被浏览器拒绝。

其实解决ssl的问题,也是上上星期才搞定,中间有段时间,烧炭煮茶不小心一氧化碳中毒,在医院躺了一周,在这里提醒一句,开窗只是能延长你进入昏迷时间,但是一氧化碳还是在的,是要在通风的环境才能烧炭,不是单纯开窗就行。不然小心上新闻。真的会死。

进入正题:

一.创建证书

  1. 生成私钥:首先,您需要生成一个私钥。您可以使用openssl命令来生成一个新的RSA私钥。请在终端中运行以下命令⁵:
openssl genrsa -out server.key 2048

这将生成一个名为server.key的2048位RSA私钥。

  1. 生成证书签名请求(CSR):接下来,您需要使用刚刚创建的私钥来生成一个证书签名请求。这可以通过以下命令完成⁴:
openssl req -new -key server.key -out server.csr

在执行此命令时,系统将提示您输入一些信息,如您的国家、省份、城市、组织名称等。最重要的是“Common Name”,它应该是您服务器的IP地址或主机名。

  1. 生成自签名证书:最后,您可以使用您的私钥和CSR来生成一个自签名证书。这可以通过以下命令完成:
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

这将生成一个有效期为365天的自签名证书server.crt

现在,您应该有一个私钥(server.key)和一个自签名证书(server.crt)。这两个文件就是您在Cowboy WebSocket SSL中需要用到的密钥。

请注意,这些步骤生成的是自签名证书,它可能不适合在公共互联网上使用。对于公共互联网的使用,您可能需要从受信任的证书颁发机构(CA)获取证书。

二.修改代码

ssl_opts()函数中,certfilekeyfile的值应该是您的证书文件和私钥文件的路径。所以,如果您的证书文件名是server.crt,私钥文件名是server.key,并且它们都在当前目录下,那么您应该这样写:

	cowboy:start_tls(
		http, 
		[
        	{port, 8080},
        	{certfile, "./server.crt"},
        	{keyfile, "./server.key"}
    	], 
		#{env => #{dispatch => Dispatch}}
	).

请注意,这里的路径应该是相对于Erlang运行时的工作目录的。如果您的证书和私钥文件在其他位置,您需要提供正确的路径。

这里要特别关注,如果你是使用rebar3,那么这里server.crt ,server.key两个文件所在位置,是在你运行 rebar3 shell 所在目录,

不是代码文件所在目录,不是代码文件所在目录,不是代码文件所在目录,重要事情说3次。

三 导入证书到浏览器

这一步非常关键,因为我们这个证书不是可信机构颁发的,所以浏览器是不承认。

  • 双击server.crt 文件,会弹出一个窗口,你会看到有个 “安装证书” 按钮。
  • 点击安装证书,会弹出 “证书导入向导”,一般选当前用户,然后下一步。
  • 非常重要不能选 “根据证书类型,自动选择证书存储”,要选“将所有证书都放入下列存储”,

​ 选择浏览,然后选 受信任的根证书颁发机构,按确定

  • 然后按下一步,导入以后,浏览器就不会提示证书有问题了。

@essen After you sent me this message, I spent some time researching. In the end, I had to use SSL WebSocket because there’s no way to prevent login authentication from being stolen. I originally wanted to organize the answers I found and post them, then I was going to make tea with charcoal. I got carbon monoxide poisoning and almost died. I was in the hospital for a few days and came out today. The official documentation may be a bit incomplete for ordinary users. It lacks the import of certificates in the browser. My English is not good, so this paragraph is translated. Thank you, author. I posted the answer I found in a self-question and answer format.