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 秒实验
- 把
-days 1改成-days 3650,看证书字节数变不变——几乎不变,有效期只是一个字段。 - 把
-nodes去掉,看私钥文件怎么变——变成加密的,启动时要输密码,自动化就没法用了。 - 把
-subj里的 CN 改成别的域名,想想会怎样——浏览器会因为域名不匹配报警告,测试时要设成访问的地址。
常见误区
- 「装上 openssl.exe 就能编 Nginx」——编要开发包,不是工具包。
- 「自签证书不能用于测试」——握手流程完全一样,只是不被信任。
- 「TLS 版本越高越慢」——1.3 少一个往返,反而更快。
自测三问(点开对答案)
1. 编 Nginx 需要 OpenSSL 的哪一部分? —— **开发包**,也就是头文件加 lib,不是命令行工具。 2. `-x509` 和 `-nodes` 各是什么意思? —— **直接出自签证书;私钥不加密**,后者是为了自动化启动。 3. TLS 1.3 比 1.2 少几个往返? —— **少一个**:1.2 两个往返,1.3 一个。- ☐ (本节完,下一节 10.2.3 Ubuntu 18 下安装 Nginx)