🎬 第十章 Nginx搭建直播平台
📄 一句话概括

Windows 没有系统自带的 OpenSSL,而 Nginx 要它才能上 HTTPS 与 RTMPS。 运行库、开发包、命令行工具是三样东西,别混。

📝 本节和前面几节的关系

10.2.1 装的是 Nginx 本体,这一节补它缺的那块。 顺序不能反:先有 OpenSSL,才编得出带 TLS 的 Nginx。

一、三个东西不要混

同一个名字下面其实有三种东西,用途完全不同:

① 运行库:DLL 文件,让已经编好的程序跑起来。 只部署不编译,装这个就够。 ② 开发包:头文件加 lib 文件,让程序编得出来。 编译 Nginx 要的是这个。 ③ 命令行工具:openssl.exe,用来生成证书、查证书链。 运维日常用得最多的反而是它。

装错的典型症状: 只装了命令行工具,然后去编 Nginx,报找不到 openssl/ssl.h。 因为头文件在开发包里,不在工具包里。

二、证书是怎么生成的

TLS 要能跑,第一步得有一张证书。 自己搭测试环境,用自签证书最省事。

一条命令就够: openssl req -x509 -newkey rsa:2048 -nodes -days 1 再加 -keyout 与 -out 指定两个输出文件。 -x509 表示「直接出自签证书」, 不加它会进入「先做请求再签发」的两步流程。 -nodes 表示私钥不加密,不然每次启动都要输密码,服务没法自动起。

自签证书在浏览器里会报警告,这是正常的: 因为签发人就是你自己,浏览器不认。 测试环境无视它即可,生产环境必须换真证书。

三、TLS 1.2 与 1.3 差在哪

两代协议最大的差别是「握手要几个来回」。

① TLS 1.2:要两个往返才谈完。 客户端先问、服务端回答并给证书、客户端验证后再回一次。 跨公网时这两个往返就是四五十毫秒。 ② TLS 1.3:一个往返就够。 它把密钥交换提前,客户端第一条消息里就带上猜测的密钥参数。 猜对了就省一个往返。

还有个看得见的变化: TLS 1.3 的密码套件名里不再写密钥交换算法,比如 TLS_AES_256_GCM_SHA384 里只有加密与摘要。 因为 1.3 把密钥交换单独拿出去做了,不再和加密算法绑在一起。

所以装新版本 OpenSSL 的实际收益是: 支持 TLS 1.3,首屏少一个往返; 以及支持更多现代密码套件。 老版本连 1.3 都协商不了,只能退回 1.2。

四、动手 Demo:装 OpenSSL——没有它,HTTPS 与 RTMPS 都无从谈起

真生成一张自签证书,起一个 HTTPS 服务, 再用 TLS 1.2 与 1.3 各握一次手,看差别。

import socket, ssl, subprocess, tempfile, threading, time

d = tempfile.mkdtemp()
cert, key = d + "/c.pem", d + "/k.pem"
subprocess.run(["openssl", "req", "-x509", "-newkey", "rsa:2048",
                "-keyout", key, "-out", cert, "-days", "1",
                "-nodes", "-subj", "/CN=127.0.0.1"], check=True)

def handshake(ver):
    sctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    sctx.load_cert_chain(cert, key)
    if ver is not None:
        sctx.minimum_version = ver
        sctx.maximum_version = ver
    srv = socket.socket()
    srv.bind(("127.0.0.1", 0))
    srv.listen(1)
    addr = srv.getsockname()

    def serve():
        c, _ = srv.accept()
        tc = sctx.wrap_socket(c, server_side=True)
        tc.recv(64)
        tc.sendall(b"hi")
        tc.close()
        srv.close()

    th = threading.Thread(target=serve)
    th.start()

    cctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    cctx.check_hostname = False
    cctx.verify_mode = ssl.CERT_NONE
    if ver is not None:
        cctx.minimum_version = ver
        cctx.maximum_version = ver
    t0 = time.perf_counter()
    cs = cctx.wrap_socket(socket.create_connection(addr, timeout=3))
    cs.sendall(b"x")
    cs.recv(64)
    el = (time.perf_counter() - t0) * 1e6
    cs.close()
    th.join()
    return cs.version(), cs.cipher()[0], el

for name, v in (("TLS1.2", ssl.TLSVersion.TLSv1_2),
                ("TLS1.3", ssl.TLSVersion.TLSv1_3)):
    vs, cp, el = handshake(v)
    print("%-7s %-8s %-30s %.0f 微秒" % (name, vs, cp, el))

实跑输出:

--------------------------------------------------------------------
装 OpenSSL——没有它,HTTPS 与 RTMPS 都无从谈起
--------------------------------------------------------------------
**① 为什么 Windows 上要单独装一个**

  **Linux 自带 OpenSSL 库,Windows 没有。**
  **而 Nginx 想要支持 HTTPS、RTMPS、以及很多签名算法,就得链上它。**
  **所以编译 Nginx 之前先装 OpenSSL,装完还要把源码目录告诉 configure。**

  **三个东西不要混:**
  **① 运行库**:**DLL,让程序跑起来。**
  **② 开发包**:**头文件加 lib,让程序编得出来。**
  **编译 Nginx 要的是这个。**
  **③ 命令行工具**:**`openssl.exe`,用来生成证书、查证书。**
  **运维最常用的是这个。**

**② 真跑:生成一张自签证书,再用它跑一次握手**

  **自签证书在浏览器里会报警告,但握手流程和真证书一模一样。**
  **自己搭测试环境用它,最省事。**

  **证书生成成功,大小如下:**

  文件                     字节
  ---------------------------------
  cert.pem(公钥证书)     1115
  k.pem(私钥)            1704

  限定版本     协商结果     密码套件                        握手微秒
  ----------------------------------------------------------------------
  TLS 1.2      TLSv1.2      ECDHE-RSA-AES256-GCM-SHA384     5512
  TLS 1.3      TLSv1.3      TLS_AES_256_GCM_SHA384          6727
  不限定       TLSv1.3      TLS_AES_256_GCM_SHA384          6837

  **每一版都跑了三次取最小值,因为第一次握手要加载证书,明显偏慢。**
  **即便如此,环回上两版的差别也只有百分之几——**
  **这点差别说明不了什么,因为环回根本没有真实的往返延迟。**
  **理论上 TLS 1.3 少一个往返:1.2 要两次才谈完,1.3 一次就够。**
  **跨公网时这个差别就是「一个 RTT」——国内通常二三十毫秒。**
  **表里 1.3 反而略慢几十微秒,这不是特性,是噪声。**

  **密码套件那一列能看出 TLS 1.3 的特点:**
  **它的套件名里不再区分密钥交换算法,**
  **因为 1.3 把密钥交换单独拿出来做了,不再和加密算法绑在一起。**

自签证书两个文件加起来不到 3 KB,够测试用。 TLS 1.3 的套件名里没有 ECDHE,因为密钥交换被拆出去了。 环回上两版差别很小,因为环回没有真实的往返延迟。 每版跑三次取最小值才准,第一次握手要加载证书,明显偏慢。

30 秒实验

  1. 把 -days 1 改成 -days 3650,看证书字节数变不变——几乎不变,有效期只是一个字段。
  2. 把 -nodes 去掉,看私钥文件怎么变——变成加密的,启动时要输密码,自动化就没法用了。
  3. 把 -subj 里的 CN 改成别的域名,想想会怎样——浏览器会因为域名不匹配报警告,测试时要设成访问的地址。

常见误区

  1. 「装上 openssl.exe 就能编 Nginx」——编要开发包,不是工具包。
  2. 「自签证书不能用于测试」——握手流程完全一样,只是不被信任。
  3. 「TLS 版本越高越慢」——1.3 少一个往返,反而更快。
自测三问(点开对答案) 1. 编 Nginx 需要 OpenSSL 的哪一部分? —— **开发包**,也就是头文件加 lib,不是命令行工具。 2. `-x509` 和 `-nodes` 各是什么意思? —— **直接出自签证书;私钥不加密**,后者是为了自动化启动。 3. TLS 1.3 比 1.2 少几个往返? —— **少一个**:1.2 两个往返,1.3 一个。

相关笔记