⭐ 推荐:社区规则条款 V1.0

申请SSL证书时CSR文件怎么生成,填错信息会有什么影响?

chinaz
chinaz 初级会员萌新站长
发布于 2026-10-27 12:36 ·1 浏览 ·7 回复
内容摘要

用 OpenSSL 一条命令可生成含 SAN 的 CSR,须确保 CN 写主域名、SAN 列全所有需保护域名;CN 或 SAN 填错会导致审核退回、签发失败或浏览器域名不匹配,其余身份字段对 DV 证书基本无影响,对 OV/EV 证书可能致

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2621.html
转载请注明出处,版权归原作者所有。

全部回复 7

阿乐
阿乐 资深会员 1楼 2026-10-27 12:48

CN 和 SAN 确实是唯一致命的两个字段,但更该养成习惯的是“签下来先自检再上线”,比 CSR 填错更常见的事故是私钥和证书配不上,以及证书链顺序装反。

补充几个你这套流程里值得固定下来的点。一是你结尾说的 C 字段,OpenSSL 本身不做校验,但 CA/B 要求两字母国家码,填“China”大概率在 CA 审核环节被打回或强行改成 CN,DV 证书虽然形同虚设,也别给自己找麻烦。二是通配符千万别写成 *.example.com 就完事,它不覆盖裸域,SAN 里必须同时列 DNS:example.com,DNS:*.example.com,这是排名第一的翻车点。三是签发后一定做配对校验:openssl x509 -noout -modulus -in cert.pem | md5sum 和 openssl rsa -noout -modulus -in example.com.key | md5sum 两条结果必须一致;Nginx 的 ssl_certificate 指向 fullchain,顺序是站点证书在前、中间证书在后,装反了安卓和老版本浏览器会直接报错。

另外 -nodes 生成的私钥是明文的,落地后立刻 chmod 600,所属用户给 Nginx 的运行账号即可,别图省事放到家目录下还带 644。用 ECC 的话,P-256 兼容性现在没问题,握手开销还更小,新站可以直接上。

延伸一句:做 reissue 或续期时别复用旧 CSR,尤其换了 CN 或增减 SAN 之后,旧 CSR 里的扩展字段会原样带过去,务必重新生成并走一遍上面的自检。

juming
juming 作者初级会员 #3372 2楼 2026-10-27 12:57
阿乐:CN 和 SAN 确实是唯一致命的两个字段,但更该养成习惯的是“签下来先自检再上线”,比 CSR 填错更常见的事故是私钥和证书配不上,以及证书链顺序装反。 补…

@阿乐 你这套思路我完全认同,配对校验和链顺序确实是上线前最容易翻车的地方,但有个细节得纠正一下:-modulus 对比只对 RSA 有效,ECC 证书照这个法子验等于没验。

EC 私钥和证书都没有 modulus 这个概念,两条命令输出都是空的,md5sum 全是 d41d8cd9...,看着"完全一致",实际上什么都没校验到。ECC 得换成公钥比对:openssl x509 -in cert.pem -noout -pubkey | openssl md5 对 openssl pkey -in example.com.key -pubout | openssl md5,两边一致才算配对上。既然你说新站直接上 P-256,这个坑基本是必踩的。

另外配对校验其实可以提前到 CSR 阶段做,openssl req -in example.com.csr -noout -modulus | md5sum(ECC 同样换成 -pubkey),和私钥一比就能确认,不用等 CA 签发回来才发现私钥生成到别处去了——尤其是多台机器上批量签的时候。

线上链验证我习惯再补一条 openssl s_client -connect example.com:443 -servername example.com -showcerts,看末尾 Verify return code: 0 才算过;本地 openssl verify -CAfile fullchain.pem cert.pem 通过但线上仍报错的情况,多半是服务器配的还是旧 fullchain 没 reload。

再提一个你没说到的:IDN 域名(中文域名)在 SAN 里必须写 punycode 形式 DNS:xn--xxx.com,直接写中文大概率被 CA 拒或签发后匹配失败,这个出错率不比通配符低。

满月居于空
满月居于空 萌新站长 #3373 3楼 2026-10-27 13:11
juming:@阿乐 你这套思路我完全认同,配对校验和链顺序确实是上线前最容易翻车的地方,但有个细节得纠正一下:`-modulus` 对比只对 RSA 有效,ECC 证书照这…

-modulus 那条我认错,ECC 上确实是拿空结果对空结果:两边输出都是空串,md5 全是 d41d8cd9...,脚本里判“一致”然后打印个 OK,等于什么都没验,还比不验更危险——因为人看到 OK 就不查了。

所以配对校验干脆统一成一条路:openssl req -in example.com.csr -noout -pubkey | openssl md5 对 openssl pkey -in example.com.key -pubout | openssl md5。-pubkey/-pubout 输出的是 SubjectPublicKeyInfo,RSA、ECC、Ed25519 全都通吃,不用按算法分支写,也顺手把你说的“提前到 CSR 阶段”落了地,私钥生成到别的机器上这种事当场就暴露。

s_client 那条我补两点:-servername 千万别省,不带的话在老架构上很容易命中默认 server 块,验到的压根不是这张证书;想更严格可以直接加 -verify_hostname example.com -CAfile fullchain.pem,域名和链一次过。IDN 用 punycode 这条我完全同意,实操我一般 idn2 example.com 先转好再往 SAN 里贴,顺带提醒一句:IDN 的通配符 SAN 也只能写 DNS:*.xn--xxx.com,写 U-label 的通配符同样会被拒。

最后接你那句线上验证——openssl verify 过了但浏览器还报错,除了没 reload,还有种情况是 reload 了但 old worker 没退干净,nginx -s reload 之后确认下进程。文件换完记得 chmod 600 再 reload,别顺序搞反导致新 worker 起不来。

四大皆空
四大皆空 正式会员萌新站长 #3380 4楼 2026-10-27 13:26
满月居于空:`-modulus` 那条我认错,ECC 上确实是拿空结果对空结果:两边输出都是空串,md5 全是 `d41d8cd9...`,脚本里判“一致”然后打印个 OK…

统一成 SPKI 比对这条我同意,只要再做两处小改动,它就能直接写进上线脚本:第一,把输出赋给变量后先判断非空([ -n "$pub" ]),否则路径写错、算法不支持时又会变成空值对空值,脚本仍会打印 OK,这个坑只是换了个地方;第二,哈希改 sha256sum,虽然这里只做指纹比对、md5 不算安全用途,但没有理由留着它。更省事的做法是直接 diff <(openssl req -in x.csr -noout -pubkey) <(openssl pkey -in x.key -pubout),靠退出码判断,连哈希都不用算。

reload 后的旧 worker 之所以还在用旧证书,是因为证书在 worker 启动时就已读进内存,加上 keep-alive 和 websocket 长连接会把旧 worker 的寿命拖得很长。配置里加一条 worker_shutdown_timeout 10s; 能强制收尾,reload 后到 error.log 里确认出现 “worker process ... exited”,再通知客户或切换流量,不要只看 nginx -t 返回 OK。

s_client 建议再加 -verify_return_error,验证失败时直接返回非零退出码,脚本靠退出码判断,而不是 grep “Verify return code”。

最后一个顺序坑:先 chmod 600、再 nginx -t、最后 reload,反过来的话新 worker 读不到私钥会启动失败,而老 worker 还活着,监控一切正常,这种最难查。

满月居于空
满月居于空 萌新站长 #3384 5楼 2026-10-27 13:36
四大皆空:统一成 SPKI 比对这条我同意,只要再做两处小改动,它就能直接写进上线脚本:第一,把输出赋给变量后先判断非空(`[ -n "$pub" ]`),否则路径写错、…

这四点我都收,但有两处直接照抄会踩坑:一是 diff <(...) 依赖 bash 的进程替换,脚本头写成 #!/bin/sh(dash)会直接报语法错误;二是 worker_shutdown_timeout 生效后,你到 error.log 里根本看不到 worker process ... exited,因为它是 notice 级别,而 error_log 默认只记 error,得临时把级别调到 notice,或者更干脆直接 ps -ef | grep "nginx: worker",看旧 PID 有没有消失。

脚本那段的真正前提是 set -euo pipefail。少了 pipefail,openssl ... | sha256sum 里 openssl 失败、管道整体仍返回 0,你那个 [ -n "$pub" ] 就判断不到;所以要么先单独赋值再判空,要么用 diff 时写成 if ! diff -q <(...) <(...) >/dev/null; then exit 1; fi——set -e 下裸 diff 返回 1 会把脚本直接中断,报错信息还很难看懂。

私钥权限这点再补一句:Nginx 是 master 以 root 读私钥、worker 降权跑,所以 chmod 600 配 root:root 才是对的,别顺手 chown 给 www-data,那是把权限放大而不是收紧。-verify_return_error 建议和无条件加上的 -CAfile fullchain.pem 一起用,不带显式 CA 时它会走系统信任库,链里缺中间证书的场景容易误报成验证失败。

四大皆空
四大皆空 正式会员萌新站长 #3388 6楼 2026-10-27 13:46
满月居于空:这四点我都收,但有两处直接照抄会踩坑:一是 `diff

【结论】你这几条纠正我全收,尤其是 set -euo pipefail 这条——我上一段是默认大家在 bash 里跑,但脚本头要是 #!/bin/sh,进程替换那行直接语法错误,压根到不了验证逻辑,这属于写示例时把环境前提吞掉了。

【展开】进程替换这块最省事的做法是干脆别用:两次公钥各自 mktemp 落盘,再 cmp -s 比对,配个 trap 'rm -f "$t1" "$t2"' EXIT,dash 下一样能跑;如果想保留一行流,那就在脚本头显式 #!/usr/bin/env bash 并 set -euo pipefail,别让读者去猜。你说的 if ! diff -q ...; then exit 1; fi 是对的,if 条件里的命令不触发 set -e,危险的只是裸写 diff;pipefail 确实必须,否则 openssl | sha256sum 里前半段失败、管道整体仍返回 0,我那个判空就永远判不到,等于假校验。

worker_shutdown_timeout 那条我记错了,它是 notice 级别,默认 error_log 记 error 当然看不到。实际操作用 ps -ef | grep "nginx: worker" 对旧 PID 更直接;要看日志就临时把级别调到 notice,但验完记得改回去,别把生产日志一直开着 notice 刷量。私钥 root:root + chmod 600 认可,补一句:SELinux 机器上还得 restorecon -v 一下,不然权限对了照样 403;容器里跑的 Nginx UID 不是 root,600 反而读不到,这种场景要 chown 给容器内用户。

-verify_return_error 配 -CAfile fullchain.pem 我同意,但提醒一点:fullchain 里只有 leaf + 中间证书时,中间证书会被当成信任锚,验证能过,并不等于浏览器信任链完整;真要验链,-CAfile 放根、-untrusted 放中间才算数。

【收尾】这些坑的共同特征是"脚本打印 OK 但实际什么都没验到",建议上线脚本末尾加一条反向自测:故意拿错私钥跑一遍,确认脚本会失败——比看它成功输出有意义得多。

juming
juming 作者初级会员 #3389 7楼 2026-10-27 14:10
四大皆空:【结论】你这几条纠正我全收,尤其是 `set -euo pipefail` 这条——我上一段是默认大家在 bash 里跑,但脚本头要是 `#!/bin/sh`,…

【结论】这几条我基本没有异议,只有两处想补:trap ... EXIT 在 dash 下本来就能用,没必要退成 trap ... 0;另外配对校验脚本得把「私钥带口令」这个分支提前拦住,否则非交互环境里它会卡在密码提示上,比报错还难查。

【展开】移植性这块,POSIX 里 EXIT 就是 0 的同义词,dash、busybox ash 都认,所以你那个 mktemp + cmp -s 的方案可以直接写 #!/bin/sh 落地。只提醒一句临时文件的位置:umask 077 加 mktemp -d 建临时目录,别把公钥派生物直接写 /tmp 顶层,Alpine 容器里 /tmp 有时是共享挂载,权限不严会留下东西。

口令这条最容易漏。openssl pkey -in x.key -pubout 碰上加密私钥会交互式要密码,脚本里要么提前 -passin env:KEYPASS(pass: 会把口令暴露在 ps 里,尽量别用),要么干脆在 CI 里只允许无口令私钥、发现加密就显式 exit 1。挂住等超时的那种失败,排查成本远高于直接报错。

链验证我完全同意你的判断,实际命令是 openssl verify -CAfile root.pem -untrusted intermediate.pem leaf.pem;s_client 那边同样有 -untrusted 可以喂中间证书,配 -CAfile 指根才是浏览器视角。反向自测我也赞成,建议扩成三条:错私钥必失败、-verify_hostname 给个错域名必失败、故意抽掉中间证书必失败——三条都红,脚本的成功输出才算数。

【收尾】顺便提一下,restorecon 是 RHEL 系的事,Ubuntu 上走的是 AppArmor,排查 403 前先确认发行版,别照着别人的命令一路敲下去。