14.4.4. CA 签名(公开受信任)证书

当你同时控制两端时,自签名证书是可行的。如果情况相反,任意客户端(浏览器、手机、第三方软件)必须连接到摄像头,而无需被告知去信任某个自定义证书,那么该证书就必须由这些客户端已经信任的某个公共证书颁发机构(CA)签名。摄像头上的 TLS 代码与自签名情形完全相同——用 load_cert_chain 加载 DER 格式的证书和密钥——只是你获取该证书的方式发生了变化。

最重要的一点:你自己生成私钥,并且它绝不离开你的机器。CA 永远看不到它。你发送给 CA 的是一份证书签名请求(CSR)——一个包含你的公钥和域名的小文件——而你拿回来的是一份证书(你的公钥和名称,由 CA 签名)。密钥和证书是由两个独立步骤产生的两个独立文件;CA 始终只处理公钥那一半。

整个流程,全部在普通机器上完成(绝不在摄像头上):

  1. 获取域名。公共 CA 为你控制的某个 DNS 名称(例如 cam.example.com)签发证书;它们不会为裸 IP 地址或像 mycam 这样仅限本地的名称签发证书。

  2. 生成密钥和 CSR。一条 OpenSSL 命令即可产生私钥和与之匹配的 CSR。使用你为自签名证书所用的同一种密钥类型(参见 概念:信任、密钥与文件格式);推荐 ECDSA P-256。

    ECDSA P-256——推荐:

    openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
        -nodes -keyout domain.key -out domain.csr \
        -subj "/CN=cam.example.com" \
        -addext "subjectAltName=DNS:cam.example.com"
    

    ECDSA P-384——更强,但更大/更慢:

    openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 \
        -nodes -keyout domain.key -out domain.csr \
        -subj "/CN=cam.example.com" \
        -addext "subjectAltName=DNS:cam.example.com"
    

    RSA-2048——最大兼容性:

    openssl req -new -newkey rsa:2048 \
        -nodes -keyout domain.key -out domain.csr \
        -subj "/CN=cam.example.com" \
        -addext "subjectAltName=DNS:cam.example.com"
    

    保密 domain.key——这是你最终要放到摄像头上的密钥文件。domain.csr 是你交给 CA 的文件;它不包含任何机密。

  3. 提交 CSR 并证明你控制该域名。这正是两种常见途径的不同之处:

    • Let's Encrypt 这样的自动化 ACME CA,由 certbotacme.sh 之类的工具驱动,会替你完成步骤 2 和 3:它生成密钥、构建 CSR、自动应答质询(HTTP-01:通过该域名上的 80 端口提供一个令牌,或 DNS-01:在其 DNS 中发布一条 TXT 记录),并写出最终的文件。

    • 商业 CA(直接购买或通过域名/主机经销商购买):你把 domain.csr 文本粘贴到一个网页表单中,然后通过回复一封验证邮件、发布一条 DNS 记录,或在该域名的某个 Web 服务器上放置一个文件来证明控制权。验证通过后即可下载签发的文件。

  4. 收集签发的文件。为了理解你收到的内容,了解证书构成一条信任链会有帮助:你域名的证书由一个中间 CA 签名,而后者又由一个根 CA 签名。每一环都为它下面的一环担保。你最终会得到:

    • 你的私钥(来自步骤 2)。CA 从未拥有它;它留在你的机器上,是你最终要放到摄像头上的密钥。

    • 证书——也称为终端实体服务器证书。这是针对你特定域名(cam.example.com)的证书:它包含你的公钥和你的名称,并由 CA 的中间证书签名。这正是摄像头出示以表明自己身份的证书。

    • 一份或多份中间 CA 证书(即"链"或"CA 捆绑包")。CA 不会直接用它的根证书签发你的叶证书——根证书的密钥是离线保存且受到严密保护的——所以它用一个中间证书签名,而该中间证书本身又由根证书签名。中间证书就是把你的叶证书向上连接到根证书的那一环。

    证书是信任锚:一份属于 CA、位于证书链顶端的自签名证书。你不会拿到它,也永远不会部署它,因为每个客户端都已经拥有它——操作系统、浏览器、手机和语言运行时都附带一个内置的根证书"信任库"。客户端通过遍历证书链来信任你的叶证书:它已经信任根证书,根证书为中间证书担保,而中间证书又为你的叶证书担保。(这正是在自签名情形中你那唯一的 server.der / cafile 所承担的工作——在那里就是你自己的根。)

    fullchain 文件就是把叶证书和中间证书拼接到一个文件中,叶证书在前,并刻意不包含根证书(发送根证书毫无意义——客户端只信任它已经拥有的根证书)。普通服务器会出示整条 fullchain,以便任何客户端都能遍历它。摄像头做不到:它只加载并出示一份证书——叶证书——而无法同时发送 CA 给你的那些中间证书。

    你实际会见到的文件名:像 certbot 这样的 ACME 工具会写出 privkey.pem(你的密钥)、cert.pem(仅叶证书)、chain.pem(仅中间证书)和 fullchain.pem(叶证书 + 中间证书)。商业 CA 通常给你一个用于叶证书的 .crt 和一个用于中间证书的 .ca-bundle,而 .key 则是你自己生成的那个。

  5. 转换并复制。将私钥和叶证书转换为 DER,并完全按照 自签名证书 上的方式复制到摄像头。摄像头随后将它们作为自己的服务器证书出示,标准客户端会自动接受连接,因为它们已经信任该 CA——无需任何客户端侧的配置。

小技巧

实践中,摄像头只出示叶证书(而从不出示中间证书)会有如下表现:

  • 已经缓存了 CA 中间证书的客户端——主流浏览器和 HTTPS 库通常都缓存了——会自行补全证书链并正常连接。

  • 依赖服务器来提供中间证书的客户端,在与摄像头握手时会失败。

如果每一种可能的客户端都必须成功,就不要直接在摄像头上终结公共 TLS。在它前面放一个网关/反向代理,由它向外界提供完整的证书链,并让该代理通过上面描述的自签名流程来访问摄像头。