Skip to content

🎫 证书是怎么工作的

先想一个生活里的场景:

你去银行柜台取钱。柜员凭什么相信你是你?

靠身份证。而柜员之所以信这张卡片,不是因为它印得好看,是因为上面有公安机关的防伪印章——柜员不认识你,但他认公安机关。

HTTPS 证书,就是网站的身份证。这篇文章从头到尾只用这一个比方,把它讲透。

读完你会知道

  • 证书到底证明了什么,为什么说它根本不负责加密
  • 信任链为什么是三层,fullchain.pemcert.pem 差在哪(最高频的坑
  • .crt .cer .pem .key .p12 这堆后缀怎么分——以及为什么它们其实没区别
  • 申请证书时那三种验证方式该选哪个
  • 私钥泄露了到底能不能补救(剧透:基本不能)

这篇是前置阅读

如果你要配的是 Cloudflare 那条链路,读完这篇再去看 👉 Cloudflare 回源 SSL 与源站证书,那边讲的是这些证书在真实链路里怎么摆


一、证书就是网站的身份证

一句话定义:

证书 = 公钥 + 身份信息 + CA 的数字签名

对着身份证看一遍,就全明白了:

身份证上的证书里的作用
你的照片公钥用来核对"是不是本人"
姓名、住址域名(example.com)你是谁
公安机关的防伪印章CA 的数字签名凭什么信这张卡
有效期 2020–2040有效期过期作废

CA(Certificate Authority,证书颁发机构)就是发身份证的公安局。它干的活只有一件:核实你确实是你,然后盖个章。

最容易搞反的一点

证书解决的是身份,不是加密

很多人以为"装了证书才能加密"。反了。

没有证书,HTTPS 照样能加密。问题在于你不知道对面是谁

打个比方:你接到一个电话,信号非常清晰,没有任何人能窃听(这就是加密)。但对面自称是公安局——你怎么知道他不是骗子?

电信诈骗成立的关键从来不是"通话被监听",而是"你信错了人"。证书就是用来防这个的。

底下的三块积木

不用懂数学,知道它们各自像什么就够了:

积木生活里的对应一句话
哈希文件的"指纹"合同改一个字,指纹完全不一样
非对称加密一把公开的锁 + 一把私藏的钥匙锁能随便发,钥匙只有你有
数字签名你的亲笔签名只有你签得出,但谁都能核对

加密和签名,方向是反的

加密像小区门口的快递柜:谁都能往里放件(用公钥加密),只有你能取出来(私钥解密)。保护的是内容

签名像在合同上按手印:只有你按得出来(私钥签名),但谁都能拿去比对(公钥验证)。证明的是来源

证书用的是签名。 CA 做的事就是:在"这个域名 + 这把公钥"这句话下面,按了个只有它按得出来的手印。


二、身份证上都印了什么

证书的标准格式叫 X.509。用 openssl x509 -text 打开一张,真正要看的就这几栏:

字段相当于身份证上的踩坑点
Subject姓名现在浏览器已经不看这栏了
Subject Alternative Name姓名 + 所有曾用名真正生效的是这栏,漏一个域名就报错
Issuer签发机关(XX 分局)要和上级对得上
Validity有效期起止服务器时间不对也会失败
Public Key照片RSA 2048 或 EC P-256
Basic Constraints是不是"能给别人发证的机关"普通证书这栏必须是 FALSE
Key Usage这张证能办什么业务serverAuth 就不能用于 HTTPS
Signature防伪印章本身验证时验的就是它

CN 是历史遗留,现在只认 SAN

早年靠 Subject 里的 CN 那栏匹配域名。后来一张证书要覆盖多个域名,就加了 SAN(Subject Alternative Name),相当于身份证背面附一页"曾用名清单"。

从 2017 年起 Chrome 彻底忽略 CN,只翻那页清单。所以哪怕 CN 写对了,清单里漏了域名照样红页。

证书是公开的,私钥不是

这两样东西经常被搞混,但性质天差地别:

证书(.pem / .crt私钥(.key
好比身份证复印件本人
能给别人吗能,它本来就全球公开绝对不能
泄露了会怎样没事整套作废,重来

证书里没有私钥

证书本来就公开躺在 CT 日志里,任何人都能查到你签过哪些证书。发到网上都没事。

私钥是另一个文件,躺在你服务器上,权限必须是 600

分不清这两个,就会出现"把 .key 发到群里"这种事故。


三、信任链:为什么要三层

浏览器不认识你的网站,就像银行柜员不认识你。那它凭什么信?

因为它认识给你发证的那个机关。

  • 根证书:自己给自己盖章(因为它就是最高权威),出厂时就装在你手机和电脑里了
  • 中间证书:CA 天天干活用的那一层
  • 叶子证书:你的域名证书

为什么公安部不直接给你发证

因为最高权威的那枚印章太重要了,得锁在保险柜里离线保管。一旦泄露,全世界的手机电脑都得更新一遍才能撤销它——现实中根本做不到。

所以设计成:根只授权中间,中间天天在线发证。中间那层出事,吊销那一张就完了,根安然无恙。

fullchain 和 cert 差在哪 ⭐

这是配置里最高频的坑,也是这篇文章最有用的一节。

回到柜台:你递过去一张身份证,但柜员只认识公安部,不认识你家那个分局。所以你还得把"公安部授权此分局"的那封介绍信一起递上去

Let's Encrypt 给你四个文件,就是这个意思:

文件里面装的相当于
cert.pem只有你的证书❌ 只递身份证,柜员看不懂签发机关
chain.pem只有中间证书只有那封介绍信
fullchain.pem你的证书 + 介绍信Nginx 填这个
privkey.pem私钥你本人,永远不递出去
nginx.conf
nginx
ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

为什么"我用浏览器测着好好的"

Chrome 这类浏览器是个特别热心的柜员:你没带介绍信,它会自己打电话去分局核实(读证书里的 AIA 字段主动抓取)。所以你少配了中间证书,浏览器看着一切正常。

curl、Java 客户端、Go、老安卓、各种 SDK 是不会打这个电话的,它们直接把你打发走:

unable to get local issuer certificate

结论:永远用 fullchain.pem 别拿浏览器当验收标准——它太好说话了。


四、验证的时候到底在查什么

柜员接过证件后,会按顺序核对:

步骤柜台上技术上
1印章是真的吗用上级公钥逐级验签
2这个机关我认吗链条尽头要能落到本机信任库里
3过期了吗比对 notBefore / notAfter
4名字对得上吗SAN 里有没有当前域名
5挂失了吗查 OCSP(见第八节)
6有没有登记备案查 CT 透明日志(Chrome 强制)

任何一步没过 → 红色警告页。

顺便说个反直觉的:验完身份,双方还会另外对暗号

证书验过了,只说明"对面确实是 example.com"。真正用来加密聊天内容的密钥,是握手时临时协商出来的另一套,跟证书里那把公钥没关系

好比:你验完对方的工作证,进了会议室,然后你们当场约定了一套只有今天有效的暗号,聊完就作废。

这就是前向保密——哪怕多年后服务器私钥泄露了,攻击者当年录下的那段对话,照样解不开


五、证书的种类

按"查得多严"分

类型查什么好比
DV只确认你能进这个门你有这套房的钥匙
OV额外核实公司实体还查了你的房产证
EV最严格的人工核验上门 + 查户口

Let's Encrypt 属于 DV,免费、几秒钟签发。

别为 EV 花钱

三者的加密强度一模一样——门锁质量完全相同,区别只在办证时查得多严。

而且浏览器早就取消了 EV 的绿色地址栏(Chrome、Firefox 2019 年双双下线),用户根本看不出区别。EV 的商业价值基本归零了。

按"能罩多大范围"分

类型覆盖好比
单域名example.com一张门禁卡开一个门
SAN 多域名一张证书塞多个域名一张卡开指定的几个门
通配符*.example.com一张卡开某一层的所有门

通配符只管一层,别想当然

*.example.com 能开 www.example.comapi.example.com——同一层的门

但它开不了 a.b.example.com(又深了一层),也开不了裸域 example.com(那是楼本身,不是这层的门)。

所以标准做法是:SAN 里同时写 example.com*.example.com 两条。


六、文件格式:最劝退的部分

格式本质长什么样常见后缀
PEM文本-----BEGIN CERTIFICATE-----.pem .crt .cer .key
DER二进制记事本打开是乱码.der .cer
PKCS#12二进制打包,带密码证书 + 私钥装一个包.p12 .pfx
JKSJava 专用格式keytool 管理.jks
CSR文本BEGIN CERTIFICATE REQUEST.csr

后缀名根本不代表格式

.crt / .cer / .pem 就是个文件名,跟里面装什么没有必然关系。改个后缀照样能用。

好比箱子外面贴的标签——写着"衣服"的箱子里完全可能装着书。Windows 圈习惯贴 .cer,Linux 圈习惯贴 .pem,仅此而已。

要判断就打开看:开头是 -----BEGIN 就是 PEM,一堆乱码就是 DER。

PEM 文件的第一行才是真正的身份证明:

开头写着里面是
BEGIN CERTIFICATE证书
BEGIN CERTIFICATE REQUEST申请表(CSR)
BEGIN PRIVATE KEY私钥(通用格式)
BEGIN RSA PRIVATE KEY私钥(仅 RSA)
BEGIN EC PRIVATE KEY私钥(椭圆曲线)

一个 PEM 文件里可以连着放好几段——fullchain.pem 就是"身份证 + 介绍信"两张纸订在一起,顺序不能反:自己的在前,介绍信在后。


七、怎么申请下来的

关键在于:第一步是在你自己电脑上做的,私钥从头到尾没离开过本地。

好比办证时你只交了申请表和照片(CSR 里只有公钥),(私钥)始终在你自己这。申请表半路被人捡走了也没用——照片人人可见,但没法冒充你。

三种"核实域名"的方式

certbot、acme.sh 这些工具把"核实"这步自动化了,方式有三种:

方式怎么证明这域名是你的好比能签通配符
HTTP-01在网站指定路径放个文件上门核验,你必须在家开门
DNS-01加一条 TXT 解析记录去派出所登记,不用在家
TLS-ALPN-01走 443 的特殊握手走后门核验

怎么选:

  • 普通网站、80 端口开着 → HTTP-01,最省事
  • 通配符,或者服务器根本不暴露在公网(内网服务、K8s)→ DNS-01,唯一选择
  • 80 被占了又不想动 → TLS-ALPN-01

为什么只有 DNS-01 能签通配符

"上门核验"只能证明这一个门是你的;而去派出所登记证明的是整栋楼的产权。

要一张卡开这一层所有的门,光让人上门敲一间屋子的门显然不够。

有效期为什么越来越短

证书有效期一路被压缩:早年 5 年 → 3 年 → 2 年 → 1 年 → Let's Encrypt 的 90 天,行业还在往 47 天推进。

两个原因:

  1. 万一钥匙丢了,能被冒用的时间更短(下一节会讲,"挂失"这套基本是坏的)
  2. 逼你自动化——一年一次的事人会忘,一个半月一次的事只能写脚本

手动续期这条路已经走不通了

不管你用 certbot 还是 acme.sh,装完第一件事是确认定时任务真的在跑,并且 reload 的钩子配对了。

见 👉 Nginx 里 acme.sh 自动续期那节。


八、"挂失"这套机制,基本是坏的

私钥泄露了怎么办?理论上通知 CA 把证书吊销掉——就像身份证丢了去挂失

现实是这套机制几乎不起作用:

机制相当于为什么不好使
CRL柜台后面贴着一沓挂失名单名单好几 MB,柜员根本不翻
OCSP柜员打电话去派出所慢,而且暴露了你在哪办业务
OCSP Stapling你自己带一张昨天开的"此证有效"证明✅ 又快又不泄露隐私

OCSP 的致命伤:电话打不通就放行

浏览器查挂失状态时,如果超时或失败,默认直接放行

因为反过来做的话,CA 的服务器一抖动,全网站点集体打不开——没有哪个浏览器厂商敢这么干

结果就是:攻击者只要把这通电话掐掉,挂失检查就等于不存在。

Stapling 是让服务器自己定期去开好证明、握手时一并递上:

nginx.conf
nginx
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;

正因为挂失靠不住,行业真正的解法是让证件本身短命。 这就是 90 天的根本原因——与其指望挂失系统,不如让身份证三个月就自动作废。


九、自己发证:内网、工牌和对暗号

自签名证书

自己在家用打印机做了张身份证。你自己认,银行不认。

技术上就是 Issuer 和 Subject 是同一个——自己给自己盖章。浏览器必然报警,这是正常的,不是你配错了。

只适合本地开发和纯内网。

私有 CA

公司自己发的工牌。 你搞一张自己的根证书,手动导入到公司所有机器的信任库里,然后用它给内部服务发证。

公司里畅通无阻,出了公司一文不值。

内网服务、K8s 组件间通信走的都是这套。Cloudflare 的 Origin CA 本质上也是私有工牌——只有 Cloudflare 认它

mTLS 双向认证

平时是单向的:你验银行的身份,银行不验你的证件(靠密码认人)。

mTLS 是双向的——两边都要出示证件,像特工接头对暗号

nginx.conf
nginx
ssl_client_certificate /etc/nginx/ca.crt;
ssl_verify_client on;

用在 API 网关、服务网格(Istio)、企业 VPN:证书本身就是通行证,拿不出来连门都进不了。


十、常见报错速查

报错真实原因怎么修
unable to get local issuer certificate介绍信没递(缺中间证书)换成 fullchain.pem
NET::ERR_CERT_AUTHORITY_INVALID自制证件 / 工牌没导入导入根证书,或换公共 CA
NET::ERR_CERT_COMMON_NAME_INVALID名字对不上(SAN 里没这域名)重签,把域名加进 SAN
certificate has expired过期了,或服务器时间不对date 看一眼再说
key values mismatch证件和本人不是一个见下面的 modulus 比对
SSL_ERROR_NO_CYPHER_OVERLAP双方语言不通(协议/套件)老客户端 vs 只开 TLS 1.3
Cloudflare 526源站证书没通过校验Cloudflare 那篇

十一、openssl 排错命令

摊开看一张证书都写了什么:

bash
openssl x509 -in fullchain.pem -noout -text

只看有效期和域名清单(最常用):

bash
openssl x509 -in cert.pem -noout -dates -ext subjectAltName

远程查一个站点,-showcerts 能看出介绍信递没递

bash
openssl s_client -connect example.com:443 -servername example.com -showcerts

确认证书和私钥是不是一对——下面两条命令输出的 md5 一样才对:

bash
openssl x509 -noout -modulus -in cert.pem | openssl md5
bash
openssl rsa -noout -modulus -in privkey.pem | openssl md5

看看申请表里填了什么:

bash
openssl req -in request.csr -noout -text -verify

PEM 转 PKCS#12(给 Java / Windows 用):

bash
openssl pkcs12 -export -out bundle.pfx -inkey privkey.pem -in cert.pem -certfile chain.pem

DER 转 PEM(拿到乱码文件时):

bash
openssl x509 -inform DER -in cert.der -out cert.pem

-servername 千万别漏

现在一个 IP 上跑几十个网站是常态。好比一栋写字楼共用一个门牌号,你进门得先跟前台说"我找 A 公司"(这就是 SNI),前台才知道该给你哪家的接待。

s_client 不加 -servername 就等于闷头往里走,前台默认把你领到物业办公室——然后你对着一张不相干的证书排查半天。


十二、只记三句话

总结

  1. 证书是身份证,不是锁。私钥才是命根子——证书随便公开,.key 泄露就得全部重来
  2. 递证件要连介绍信一起递,用 fullchain.pem——浏览器会热心地帮你补,curl 和 Java 不会
  3. 挂失机制靠不住,所以证件越来越短命——自动续期不是可选项,是必需品

想看这些证书在真实链路里怎么摆(Cloudflare 回源、Origin CA、Let's Encrypt DNS-01 实操),见 👉 Cloudflare 回源 SSL 与源站证书

技术笔记