Elixir SSL服务端-客户端连接握手错误排查求助
Elixir TLS握手错误与
:ssl.listen/2参数配置指南 一、:ssl.listen/2核心必填参数
:ssl.listen/2接收两个参数:第一个是监听端口(整数),第二个是关键字列表形式的配置选项。服务端启用TLS必须包含以下关键选项:
:certfile: 服务器证书文件的路径(例如"cert.pem"):keyfile: 服务器私钥文件的路径(例如"key.pem")
如果需要强制客户端证书认证,还需添加:cacertfile(指定信任的CA证书)和:verify: :verify_peer选项,但玩具项目的单向认证场景(仅服务端向客户端证明身份)中,前两个参数即可满足需求。
二、修正后的最小复现代码
服务端代码
# lib/tls_question/server.ex defmodule TLSQuestion.Server do def start(port \\ 4000) do # 确保SSL应用已启动 Application.ensure_all_started(:ssl) opts = [ certfile: "cert.pem", keyfile: "key.pem", # 指定安全的TLS版本 versions: [:tlsv1_2, :tlsv1_3], # 单向认证场景下关闭客户端证书验证 verify: :verify_none, active: false, reuseaddr: true ] case :ssl.listen(port, opts) do {:ok, listen_socket} -> IO.puts("TLS server listening on port #{port}") accept_loop(listen_socket) {:error, reason} -> IO.puts("Listen failed: #{inspect(reason)}") end end defp accept_loop(listen_socket) do {:ok, socket} = :ssl.accept(listen_socket) IO.puts("Client connected") # 示例:接收客户端数据后关闭连接 case :ssl.recv(socket, 0) do {:ok, data} -> IO.puts("Received: #{data}") {:error, reason} -> IO.puts("Recv error: #{inspect(reason)}") end :ssl.close(socket) accept_loop(listen_socket) end end
客户端代码
# lib/tls_question/client.ex defmodule TLSQuestion.Client do def connect(host \\ 'localhost', port \\ 4000) do Application.ensure_all_started(:ssl) opts = [ # 信任自签证书,指定服务端证书作为CA证书 cacertfile: "cert.pem", versions: [:tlsv1_2, :tlsv1_3], active: false ] case :ssl.connect(host, port, opts) do {:ok, socket} -> IO.puts("Connected to TLS server") :ssl.send(socket, "Hello from client") :ssl.close(socket) {:error, reason} -> IO.puts("Connect failed: #{inspect(reason)}") end end end
注意::crypto和:ssl是Erlang/OTP自带的应用,无需在mix.exs中额外添加依赖。
三、自签证书作为CA链的合理性
将自签证书传给客户端的:cacertfile是测试场景下的正确操作。由于自签证书未被公共CA机构信任,客户端必须明确将其加入信任列表才能完成证书验证。若客户端未指定:cacertfile且未禁用验证(verify: :verify_none),会直接触发证书验证失败的握手错误。
测试时也可临时用verify: :verify_none绕过证书验证,但此做法会失去TLS的身份认证能力,仅适合快速调试,禁止在生产环境使用。
四、FunctionClauseError排查要点
该错误通常由参数格式或配置错误导致,优先排查以下方向:
- 选项关键字拼写错误:例如将
:certfile误写为:cert_file - 文件路径错误:确认证书/密钥文件位于项目根目录,或使用绝对路径指定
- 证书文件有效性:用
openssl x509 -in cert.pem -text -noout命令验证证书格式是否正确 - SSL应用未启动:调用
:ssl.listen前需确保:ssl应用已启动,可通过Application.ensure_all_started(:ssl)强制启动
内容的提问来源于stack exchange,提问作者Freddie Woodruff
相关产品推荐
相关产品推荐

