ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenSSL自建CA与多域名/IP证书签发完全指南

OpenSSL自建CA与多域名/IP证书签发完全指南 不想被浏览器天天弹“不安全”又不想每年花钱买公共CA证书或者干脆就是内网一堆服务需要HTTPS加密——那自建CA基本是绕不开的活儿。用OpenSSL搭一套自己的CA体系给多域名、多IP签发证书这算是运维和开发都得掌握的基础技能。这篇就把我的完整操作流程和踩过的坑都摊开讲从零开始一步步搭。1. 方案选型为什么自己动手搭CA1.1 自建CA相比公共CA证书的取舍先说说在什么场景下值得自建CA而不是直接去云厂商或者Lets Encrypt申请免费证书。公共CA证书最大的优势是“天然被信任”。浏览器、操作系统里预置了它们的根证书部署上去就能获得绿锁用户无感。但缺点也很明显免费证书有效期短Lets Encrypt只有90天必须配套自动续期不支持纯内网IP或自定义域名——你想给192.168.1.10或者gitlab.internal这种地址签发证书公共CA基本不搭理还有些特殊场景比如物联网设备、离线环境、需要给大量内部服务统一加密用公共CA反而不方便。自建CA相当于你自己当“发证机构”。客户端只要安装了你的CA根证书就会信任你签发的所有子证书。这在企业内部、开发测试环境、以及需要长期稳定证书的场景下非常实用。代价就是需要手动管理根证书的分发和更新这活儿做一次能管十年我觉得很值。1.2 整体证书签发流程梳理OpenSSL签发证书的完整链路其实不复杂理清楚之后后面操作就不会乱。整个流程分三步走第一步建立根CA生成根CA的私钥然后用私钥自签名生成一个根证书。这个根证书是整个信任链的源头私钥必须妥善保管。第二步生成服务端私钥和证书签名请求为你的服务比如Nginx、某个内网应用生成一对密钥然后用私钥生成CSRCertificate Signing Request证书签名请求。CSR里包含服务域名或IP等身份信息。第三步用根CA给CSR签名根CA用自己的私钥给这个CSR“盖章”生成最终的服务器证书。这个证书就带有了根CA的签名客户端只要信任根CA就信任这个证书。听起来不复杂但实际操作中有很多细节容易翻车比如证书扩展字段没配好导致浏览器报“证书缺少主题备用名称”或者根私钥权限没设对导致安全隐患。下面我会一步步带你走完整个流程并把配置文件完整贴出来。2. 环境准备与OpenSSL基础配置2.1 OpenSSL版本选择和安装不同Linux发行版的OpenSSL版本差异较大。优先选择1.1.1或3.x版本这两个版本对现代TLS协议TLS 1.3和多域名扩展支持很好而且语法与旧版本1.0.x相比没有太多兼容性麻烦。检查当前版本openssl version如果输出类似OpenSSL 1.1.1k FIPS 6 Apr 2020或者OpenSSL 3.0.2 15 Mar 2022都可以放心用。版本过低比如1.0.2建议先升级否则后面生成密钥时可能不支持更安全的算法。如果你是在Windows上操作建议直接使用Git自带的OpenSSL或者从SlProWeb下载Windows版本Linux和macOS一般自带。注意一个常见的坑Windows上用openssl命令时路径分隔符和配置文件路径的处理方式稍有不同最好把openssl的bin目录加到系统PATH中。2.2 理解X.509证书的关键字段在动手前把证书的几个关键概念搞清楚会更顺。X.509证书本质上就是一个结构化文件包含以下几个核心部分Subject主题证书持有者的身份信息比如CNexample.com即通用名称。对于多域名证书来说CN字段现在更多是标记性的真正起作用的是SAN字段。SANSubject Alternative Name主题备用名称这是多域名证书的核心。浏览器和操作系统验证证书时看的其实是SAN字段里列出的域名和IP而不是CN。这个字段可以在证书里列出多个域名和IP比如DNS:example.com, DNS:www.example.com, IP:192.168.1.10。Issuer颁发者是谁签发的这个证书。服务器证书的Issuer是根CA的Subject而根证书的Issuer是自己。Validity有效期证书生效和失效时间。Public Key公钥证书持有者的公钥配合服务端私钥使用。很多人在早期版本的OpenSSL里签发多域名证书时只修改了CN字段结果浏览器照样报错就是因为忽略了SAN。后面我们会重点配置SAN。3. 搭建根CA的完整实操3.1 创建目录结构和基础文件先把根CA的工作目录建好。我的习惯是在/opt/ca下建一套结构清晰的目录方便后续管理。mkdir -p /opt/ca/{certs,crl,newcerts,private,requests} touch /opt/ca/index.txt echo 1000 /opt/ca/serial chmod 700 /opt/ca/private简要说明这些目录的作用certs存放已签发的证书副本。crl证书吊销列表暂不需要可以先空着。newcertsOpenSSL默认存放新签发证书的位置。private存放私钥必须限制权限所以执行了chmod 700。requests存放外部发来的CSR文件。index.txt证书签发的索引数据库。serial维护证书序列号初始设置为1000之后每次签发证书都会自动递增。这些基础结构是标准CA的目录布局抄作业直接按这个来就行。3.2 编写根CA配置文件openssl_ca.cnf配置文件是整个CA的核心。我直接贴出我一直在用的根CA配置然后逐个说明关键点[ ca ] default_ca CA_default [ CA_default ] dir /opt/ca database $dir/index.txt new_certs_dir $dir/newcerts certificate $dir/certs/ca.crt serial $dir/serial private_key $dir/private/ca.key default_days 3650 default_md sha256 policy policy_loose x509_extensions usr_cert [ policy_loose ] countryName optional stateOrProvinceName optional localityName optional organizationName optional organizationalUnitName optional commonName supplied emailAddress optional [ req ] default_bits 2048 distinguished_name req_distinguished_name string_mask utf8only x509_extensions v3_ca [ req_distinguished_name ] countryName Country Name (2 letter code) stateOrProvinceName State or Province Name localityName Locality Name organizationName Organization Name commonName Common Name [ v3_ca ] subjectKeyIdentifier hash authorityKeyIdentifier keyid:always,issuer basicConstraints critical, CA:TRUE keyUsage critical, digitalSignature, keyCertSign, cRLSign [ usr_cert ] basicConstraints CA:FALSE keyUsage digitalSignature, keyEncipherment subjectKeyIdentifier hash authorityKeyIdentifier keyid,issuer重点讲几个容易被忽略的配置policy_loose表示策略宽松签发证书时不一定要求CSR里的每个字段都填。如果希望强制校验某些字段可以改成policy_strict并要求必须提供国家、组织等单位信息。v3_ca扩展段里的basicConstraints critical, CA:TRUE是关键。这表示该证书是CA证书能够签发其他证书。critical表示客户端必须强制校验这个字段。usr_cert扩展段是签发服务器证书时要带的扩展配置。keyUsage指定了这个证书的私钥能用来干什么数字签名、密钥加密等。3.3 生成根CA私钥和证书背景知识准备好之后执行下面两条命令cd /opt/ca openssl genrsa -aes256 -out private/ca.key 4096这条命令生成4096位的RSA私钥并用AES-256进行加密。执行后会提示你设置一个密码这个密码一定要记住之后每次用根CA签发证书时都要输入。如果你不想每次输入密码可以不用-aes256参数但安全性会降低生产环境非常不建议这么干。接着生成根证书openssl req -new -x509 -days 7300 -key private/ca.key -out certs/ca.crt -config openssl_ca.cnf -extensions v3_ca命令执行后会交互式地询问组织信息。比较关键的是Common Name我习惯填成类似My Internal Root CA这样的描述性名称方便区分。-days 7300表示这个根证书有效期20年够用很久了。到这里根CA的文件就生成了两个关键文件private/ca.key根CA的私钥必须妥善保管。certs/ca.crt根CA的证书需要分发给所有客户端。3.4 验证根证书用下面命令查看生成的根证书内容openssl x509 -in certs/ca.crt -text -noout重点看输出里的Basic Constraints是否显示CA:TRUE以及Key Usage是否包含Certificate Sign。如果这两项没问题根CA就算建好了。4. 签发多域名/IP证书的实操过程4.1 编写适用于多域名/IP的服务器证书配置文件签发服务器证书前我们要先准备一个单独的配置文件重点配置SAN扩展字段。这是支持多域名/IP的核心。以给两个域名和两个IP签发证书为例创建server.cnf[ req ] default_bits 2048 distinguished_name req_distinguished_name req_extensions req_ext prompt no [ req_distinguished_name ] countryName CN stateOrProvinceName Beijing localityName Beijing organizationName Example Inc commonName example.com [ req_ext ] subjectAltName alt_names [ alt_names ] DNS.1 example.com DNS.2 www.example.com IP.1 192.168.1.10 IP.2 192.168.1.11几个关键点prompt no设置后不再交互式询问信息直接使用文件里定义的内容适合脚本化批量处理。req_extensions req_ext指明CSR请求时要带上的扩展这里是SAN字段。alt_namesSAN列表支持DNS和IP两种类型。要加更多域名或IP直接递增编号即可。commonName我填了example.com作为主域名但再次强调现在浏览器不怎么看CNSAN才是关键。4.2 生成服务器私钥和CSR执行下面命令生成服务器私钥openssl genrsa -out server.key 2048这里没有再加密私钥因为服务端比如Nginx启动时每次都需要读取私钥如果私钥加密了就需要在启动时手动输入密码或配置额外的解密操作非常麻烦。服务器私钥本身权限设为600即可。然后用私钥生成CSRopenssl req -new -key server.key -out server.csr -config server.cnf查看生成的CSR中是否包含预期的SANopenssl req -in server.csr -text -noout | grep -A 3 Subject Alternative Name如果输出类似下面这样的内容说明CSR已经带上了多域名/IPX509v3 Subject Alternative Name: DNS:example.com, DNS:www.example.com, IP Address:192.168.1.10, IP Address:192.168.1.11这个检查步骤非常重要很多人签完证书才发现没带SAN直接在CSR阶段就能拦截掉。4.3 使用根CA签发服务器证书CSR准备好后用根CA来签名cd /opt/ca openssl ca -config openssl_ca.cnf -in /path/to/server.csr -out /path/to/server.crt -extensions usr_cert -days 825 -notext -batch逐个说明参数含义-config openssl_ca.cnf指定CA的配置文件。-in输入CSR文件路径。-out输出签好的证书路径。-extensions usr_cert指定签发时要附加的扩展这里是配置文件中定义的usr_cert段。-days 825证书有效期825天是为了兼容某些老设备对最长有效期为825天的限制。如果只用现代浏览器直接设-days 3650也行。-notext不在输出的证书文件里附带文本内容保持文件干净。-batch跳过交互式确认。执行完在/opt/ca/newcerts/目录下会多出一个证书文件同时index.txt里会新增一条记录。再验证一下签出来的证书openssl x509 -in server.crt -text -noout确认Issuer是根CASubject Alternative Name包含你配的域名和IPBasic Constraints显示CA:FALSE这套签发流程就完成了。4.4 签发带多个IP的证书的扩展写法如果你想签发的证书里IP地址特别多比如一套Kubernetes集群可能涉及几十个节点IP在alt_names里一个个写虽然可以但太低效。这种情况下更推荐的方式是先用openssl req生成一个不带SAN的CSR然后直接用openssl x509命令加载一个外部的扩展文件来签发。创建一个extensions.cnfsubjectAltName alt_names keyUsage digitalSignature, keyEncipherment extendedKeyUsage serverAuth [ alt_names ] DNS.1 k8s.example.com IP.1 10.0.0.1 IP.2 10.0.0.2 IP.3 10.0.0.3然后用这个扩展文件签发openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 825 -extfile extensions.cnf这种方式的优势是修改和追加IP非常方便改一下extensions.cnf重新签发即可不影响CA本身的数据库记录。对于证书数量多、经常变化的场景这个方式更灵活。5. 把证书部署到服务器并让客户端信任5.1 在Nginx中部署证书服务器私钥server.key与证书server.crt拿到手后Nginx配置非常简单server { listen 443 ssl; server_name example.com www.example.com; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; }一个容易遗漏的细节如果根CA和服务器证书不在同一个文件里需要把两者合并成证书链。有的服务端要求提供完整的证书链否则客户端会报“无法验证服务器证书”。可以把根证书追加到服务器证书文件末尾cat server.crt ca.crt server_fullchain.crt然后在Nginx里指向这个合并后的文件。多个中间证书就按顺序依次追加最上层是根证书。5.2 把根CA加入不同系统的信任链服务端证书配好后客户端设备必须信任你的根CA才能建立安全连接。不同系统的操作方式差异很大Windows双击ca.crt选择“安装证书”存储位置选“本地计算机”然后选择“将所有证书放入下列存储”浏览选择“受信任的根证书颁发机构”。macOS双击ca.crt导入钥匙串找到证书后右键“显示简介”把“信任”策略改为“始终信任”。Linux (Ubuntu/Debian)把ca.crt复制到/usr/local/share/ca-certificates/目录下执行sudo update-ca-certificates。Linux (CentOS/RHEL)把ca.crt复制到/etc/pki/ca-trust/source/anchors/执行sudo update-ca-trust。Android设置里搜索“安装证书”选择CA证书并导入。注意Android 7以上版本用户安装的CA证书默认不信任APP的流量部分应用需要额外配置networkSecurityConfig。iOS把ca.crt通过邮件或AirDrop传到手机点击安装描述文件然后去“通用-关于本机-证书信任设置”里开启完全信任。对于企业内部上百台机器的场景手动一台台装很不现实。可以考虑把它做成一个自解压安装包或者利用组策略、MDM工具批量下发效率高得多。5.3 验证证书部署是否成功部署完成后验证方式非常直接。浏览器访问服务地址如果不再出现证书告警地址栏显示正常的锁图标基本就成功了。命令行里可以用curl验证curl -v https://example.com如果你的机器已经信任了根CAcurl不会报错。如果还没导入根CA可以这样验证证书链是否正确curl --cacert ca.crt https://example.com另外检查证书的SAN是否与实际访问地址匹配可以用下面的命令echo | openssl s_client -connect example.com:443 -servername example.com 2/dev/null | openssl x509 -noout -text | grep -A 1 Subject Alternative Name输出的SAN列表里应该包含你访问的域名或IP否则即使证书本身有效也会报“证书与该网站不匹配”。6. 常见问题排查与实操经验速查6.1 浏览器提示“NET::ERR_CERT_COMMON_NAME_INVALID”这是最常见的错误几乎都是因为SAN字段缺失或配置错误。打开证书一看发现只有CN没有Subject Alternative Name。解决办法不用重新生成CA只要重新走一遍服务器证书签发流程确保配置文件里subjectAltName字段正确并包含你要访问的域名和IP即可。6.2 提示“无法将服务器证书链构建到受信任的根证书颁发机构”一种原因是客户端没有安装根CA证书。另一种原因是服务器没有下发完整的证书链。用下面命令查看服务器下发的证书链openssl s_client -connect example.com:443 -showcerts如果输出只有服务器证书而没有中间的CA证书就需要把根CA合并到server_fullchain.crt中并重新配置。6.3 OpenSSL版本不一致导致的兼容问题开发过程中经常遇到版本不一致的问题。比如某次我看到报错openssl version mismatch. built against 30000020, you have 30500060这是因为系统里有多个版本的OpenSSL库程序编译时用的是3.0.2运行时却加载了3.0.5的库版本不匹配。排查方法是ldd看程序链接的是哪个版本然后用LD_LIBRARY_PATH或直接升级系统的OpenSSL来对齐版本。开发环境里我通常用Docker封装固定版本的OpenSSL能彻底避免这类环境问题。6.4 根CA私钥泄漏的风险评估与对策如果你怀疑根CA私钥泄漏了要立即处理根CA私钥一旦泄漏攻击者就能签发任何域名的可信证书。应对策略是立刻吊销原来的根证书重新生成一套新的CA并分发给所有客户端。但吊销根证书的代价很大所有信任这台CA的设备都要重新导入新根证书。所以日常操作中保护ca.key的优先级最高。建议根CA私钥保存在离线机器或者加密容器里只签证书时临时挂载。私钥文件权限设置为600属主为root。为私钥设置强密码密码存储在密码管理器中。定期备份私钥并加密存储到安全位置防止硬件故障导致CA不可用。6.5 自建CA签发多域名证书的常见误区和冷门知识点多签了几个证书之后还有几个细节值得补充都属于日常容易被忽略但实打实影响使用体验的证书有效期不要设太长也不要太短。服务器证书我一般设2年或825天根证书可以设20年。太短要频繁重签太长万一出问题比如私钥泄漏、组织变动吊销和替换的成本很高。825天这个数字有历史原因是Apple对SSL证书有效期的上限要求现在虽然整体趋势是缩短证书有效期但自建场景下825天还是个不错的折中。同一套私钥可以重复用于签发多个证书。如果你签发过程中CSR或证书丢失了只要私钥还在重新生成CSR再签一个就行不需要重新生成私钥。这样做的好处是如果证书文件泄漏但私钥没泄漏证书本身无法被滥用重新签一个换上即可。Java环境下要单独导入信任库。Java应用有自己的证书信任库cacerts和操作系统的不通用。如果你的服务是Java写的记得用keytool -importcert把根证书导入JVM的信任库keytool -import -trustcacerts -alias myrootca -file ca.crt -keystore $JAVA_HOME/lib/security/cacerts默认密码是changeit操作后务必修改。QT/嵌入式环境交叉编译时注意OpenSSL的编译选项。如果做嵌入式开发用OpenSSL自建CA签发证书时会遇到交叉编译工具链里OpenSSL版本过老的问题。我遇到过在ARM开发板上部署证书服务时程序链接的OpenSSL库版本不支持某签名算法导致在开发板上无法验证证书链。建议交叉编译OpenSSL时把配置文件打开确认开启了SHA-256支持再编译一次。OpenSSL在线工具可以作为验证参考但生产环境别用在线工具生成私钥。有几次我手头没有Linux环境时用过在线的OpenSSL工具来临时生成CSR参考但私钥在第三方网站生成就等于私钥泄漏。私钥只能在可信环境里生成在线工具最多用来看证书解码信息。写在最后的几条心得这套流程走下来最值钱的经验就三条第一条配置文件比命令行参数可靠。用-subj和-addext一条命令签完确实快但一旦涉及多域名、多个IP或者要批量签几十张证书配置文件的方式好维护得多改一个字段就能重新生成还能放进Git仓库做版本管理。第二条测试环境尽量模拟真实环境。我经常遇到开发时说“证书没问题”一上生产就报错的情况原因往往是开发时证书的SAN和实际访问域名不一致。最稳妥的办法是证书签发完先用curl --cacert验证一遍再部署到服务端最后用浏览器实测一遍三步都过了再收工。第三条根CA私钥的安全级别要高于服务器私钥。服务器私钥丢了重新签一张就行根CA私钥丢了所有客户端都得重装证书。所以生产环境千万不要图省事把根CA放在经常操作的机器上。这套自建CA的方案我一直在各种内网项目和测试环境里用稳定的同时也省了不少买证书的钱。如果你也在折腾多域名、多IP的证书签发照着这篇文章的配置走一遍应该就能顺利搞定。
返回列表