You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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排查要点

该错误通常由参数格式或配置错误导致,优先排查以下方向:

  1. 选项关键字拼写错误:例如将:certfile误写为:cert_file
  2. 文件路径错误:确认证书/密钥文件位于项目根目录,或使用绝对路径指定
  3. 证书文件有效性:用openssl x509 -in cert.pem -text -noout命令验证证书格式是否正确
  4. SSL应用未启动:调用:ssl.listen前需确保:ssl应用已启动,可通过Application.ensure_all_started(:ssl)强制启动

内容的提问来源于stack exchange,提问作者Freddie Woodruff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 00:21:26