英文:
Simple SSL server-client connection in Elixir
问题
我正在研究一个玩具网络项目,想在服务器和客户端之间添加TLS层。我遇到了握手错误,正在尝试找出如何调试。
太长不看(TL;DR)可能是:“我应该向 :ssl.listen/2
传递哪些参数”,但这里有一个最小的示例。
首先,我使用 mix new tls_question
创建了一个新项目。
我在mix.exs中添加了 :crypto
和 :ssl
,如下所示:
def application do
[
extra_applications: [:logger, :crypto, :ssl]
]
end
然后,我使用以下命令生成了SSL证书:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365
并将 key.pem 和 cert.pem 移到了项目文件夹中。
接下来,我有以下最小的程序:
defmodule TlsQuestion do
@ip {127,0,0,1}
@port 4343
def main do
:ssl.start()
{:ok, listen_socket} = :ssl.listen(@port,
[ certs_keys: [
keyfile: "key.pem",
certfile: "cert.pem",
password: "CorrectHorseBatteryStaple"
],
reuseaddr: true
])
spawn(fn -> client() end)
{:ok, accept_socket} = :ssl.transport_accept(listen_socket)
{:ok, accept_socket} = :ssl.handshake(accept_socket)
:ssl.send(accept_socket, "Hello World")
end
def client() do
{:ok, connect_socket} = :ssl.connect(@ip, @port,
[verify: :verify_peer,
cacertfile: "cert.pem",
active: :once], :infinity)
message = :ssl.recv(connect_socket, 0)
IO.puts(message)
end
end
TlsQuestion.main()
然后我调用 mix run
。
错误消息可能对某些人有所启发,但对我没有帮助
== Compilation error in file lib/tls_question.ex ==
** (exit) exited in: :gen_statem.call(#PID<0.164.0>, {:start, :infinity}, :infinity)
** (EXIT) an exception was raised:
** (FunctionClauseError) no function clause matching in :ssl_config.key_conf/1
(ssl 10.8.7) ssl_config.erl:181: :ssl_config.key_conf({:keyfile, "key.pem"})
(ssl 10.8.7) ssl_config.erl:72: :ssl_config.cert_key_pair/3
(stdlib 4.2) lists.erl:1315: :lists.map/2
(ssl 10.8.7) ssl_config.erl:56: :ssl_config.init_certs_keys/3
(ssl 10.8.7) ssl_config.erl:51: :ssl_config.init/2
(ssl 10.8.7) ssl_gen_statem.erl:164: :ssl_gen_statem.ssl_config/3
(ssl 10.8.7) tls_connection.erl:150: :tls_connection.init/1
(stdlib 4.2) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
(stdlib 4.2) gen.erl:243: :gen.do_call/4
(stdlib 4.2) gen_statem.erl:900: :gen_statem.call_dirty/4
(ssl 10.8.7) ssl_gen_statem.erl:1239: :ssl_gen_statem.call/2
(ssl 10.8.7) ssl_gen_statem.erl:234: :ssl_gen_statem.handshake/2
lib/tls_question.ex:16: TlsQuestion.main/0
看起来是在证书和密钥文件方面出了问题?
我已将证书传递给客户端作为CA证书链(因为自签名证书是自身的证书链)。可能是这个问题吗?
英文:
I'm working on a toy networking project and I want to add a TLS layer between the server and the client. I'm getting handshake errors that I'm trying to figure out how to debug.
The TL;DR is probably: 'what arguments do I pass to :ssl.listen/2
' but here is the minimal example.
First I create a new project with mix new tls_question
.
I have added :crypto
and :ssl
to mix.exs like so:
def application do
[
extra_applications: [:logger, :crypto, :ssl]
]
end
I have then generated an SSL certificate with
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365
and moved key.pem and cert.pem into the project folder.
I then have the following minimal program
defmodule TlsQuestion do
@ip {127,0,0,1}
@port 4343
def main do
:ssl.start()
{:ok, listen_socket} = :ssl.listen(@port,
[ certs_keys: [
keyfile: "key.pem",
certfile: "cert.pem",
password: "CorrectHorseBatteryStaple"
],
reuseaddr: true
])
spawn(fn -> client() end)
{:ok, accept_socket} = :ssl.transport_accept(listen_socket)
{:ok, accept_socket} = :ssl.handshake(accept_socket)
:ssl.send(accept_socket, "Hello World")
end
def client() do
{:ok, connect_socket} = :ssl.connect(@ip, @port,
[verify: :verify_peer,
cacertfile: "cert.pem",
active: :once], :infinity)
message = :ssl.recv(connect_socket, 0)
IO.puts(message)
end
end
TlsQuestion.main()
From which I call mix run
.
The error message might be enlightening for some but hasn't helped me
== Compilation error in file lib/tls_question.ex ==
** (exit) exited in: :gen_statem.call(#PID<0.164.0>, {:start, :infinity}, :infinity)
** (EXIT) an exception was raised:
** (FunctionClauseError) no function clause matching in :ssl_config.key_conf/1
(ssl 10.8.7) ssl_config.erl:181: :ssl_config.key_conf({:keyfile, "key.pem"})
(ssl 10.8.7) ssl_config.erl:72: :ssl_config.cert_key_pair/3
(stdlib 4.2) lists.erl:1315: :lists.map/2
(ssl 10.8.7) ssl_config.erl:56: :ssl_config.init_certs_keys/3
(ssl 10.8.7) ssl_config.erl:51: :ssl_config.init/2
(ssl 10.8.7) ssl_gen_statem.erl:164: :ssl_gen_statem.ssl_config/3
(ssl 10.8.7) tls_connection.erl:150: :tls_connection.init/1
(stdlib 4.2) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
(stdlib 4.2) gen.erl:243: :gen.do_call/4
(stdlib 4.2) gen_statem.erl:900: :gen_statem.call_dirty/4
(ssl 10.8.7) ssl_gen_statem.erl:1239: :ssl_gen_statem.call/2
(ssl 10.8.7) ssl_gen_statem.erl:234: :ssl_gen_statem.handshake/2
lib/tls_question.ex:16: TlsQuestion.main/0
It looks like it's complaining about something I'm doing with the certificate and key files?
I've passed the certificate to the client as the CA certificate chain (since a self-signed certificate is its own certificate chain). Could that be the issue?
答案1
得分: 1
> TL;DR 大概是:'我应该传递给 :ssl.listen/2 的参数是什么'
listen(Port, Options) -> {ok, ListenSocket} | {error, reason()}
`Port` 被定义为整数:
0...65535
`Options` 被定义为列表:
Options = [tls_server_option()]
tls_server_option() =
server_option() |
common_option() |
socket_option() |
transport_option()
common_option() = ...| {certs_keys, certs_keys()} | ...
certs_keys() = [cert_key_conf()]
cert_key_conf() =
#{cert => cert(),
key => key(),
certfile => cert_pem(),
keyfile => key_pem(),
password => key_pem_password()}
注意,`cert_key_conf()` 是一个 Erlang 映射,在 Elixir 中会有如下结构:
{:ok, listen_socket} = :ssl.listen(@port,
[ certs_keys: [%{
}],
reuseaddr: true
])
继续类型描述:
cert_pem() = file:filename() = string() => 整数列表
key_pem() = filename() = string() => 整数列表
key_pem_password() = io_list() => 可能包含整数和/或二进制的嵌套列表
在 Erlang 中,string() 类型是整数列表。
Elixir Erlang
------ ------
整数列表: 单引号 双引号
二进制: 双引号, 语法 <<1,34,97>> 或 <<97,98,99>>
从上面列出的类型规范的底部开始,逐渐替代,你会得到:
{:ok, listen_socket} = :ssl.listen(@port,
[ certs_keys: [%{
keyfile: 'key.pem',
certfile: 'cert.pem',
password: 'CorrectHorseBatteryStaple'
}],
reuseaddr: true
])
我不确定你是否可以省略映射中的 `cert:` 和 `key:`。另外,文档中没有将 `reuseaddr` 列为 `Options` 列表中的有效 2 元组。
英文:
> The TL;DR is probably: 'what arguments do I pass to :ssl.listen/2'
listen(Port, Options) -> {ok, ListenSocket} | {error, reason()}
Port
is defined to be an integer:
0...65535
Options
is defined to be a list:
Options = [tls_server_option()]
tls_server_option() =
server_option() |
common_option() |
socket_option() |
transport_option()
common_option() = ...| {certs_keys, certs_keys()} | ...
certs_keys() = [cert_key_conf()]
cert_key_conf() =
#{cert => cert(),
key => key(),
certfile => cert_pem(),
keyfile => key_pem(),
password => key_pem_password()}
Note that cert_key_conf()
is an erlang map, giving you this structure in elixir:
{:ok, listen_socket} = :ssl.listen(@port,
[ certs_keys: [%{
}],
reuseaddr: true
])
Continuing with the type descriptions:
cert_pem() = file:filename() = string() => list of integers
key_pem() = filename() = string() => list of integers
key_pem_pasword() = io_list() => possibly nested list of integers and/or binaries
In erlang, the string() type is a list of integers.
elixir erlang
------ ------
list of itegers: single quotes double quotes
binaries: double quotes, the syntax <<1,34,97>>
or <<97,98,99>>
Starting at the bottom of the type specifications listed above and substituting upwards, gives you:
{:ok, listen_socket} = :ssl.listen(@port,
[ certs_keys: [%{
keyfile: 'key.pem',
certfile: 'cert.pem',
password: 'CorrectHorseBatteryStaple'
}],
reuseaddr: true
])
I'm not sure whether you can omit the keys cert:
and key:
in the map. Also, the docs don't list reuseaddr
as a valid 2-tuple in the Options
list.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论