朋友前几天给我打电话,说他们公司一个跑在公网IP上的业务系统,浏览器地址栏一直挂着“不安全”的标识,客户点进去一看就皱眉。他们想申请一张SSL证书,结果在服务商控制台翻了一圈,发现免费证书全是按域名签的,压根不支持IP;又去问客服能不能走个人认证,对方甩过来一句“IP证书需要备案”,直接把项目卡住了。
这个场景我太熟了。国内一堆企业自建系统、设备管理后台、API网关、数据大屏,根本没有域名,只有个裸IP就对外提供服务。想上HTTPS,就必须搞清楚一件事:在纯国内大陆环境下,给IP地址签发并部署SSL证书,到底卡在哪几个环节,每个环节怎么解决。
这篇文章我把整条链路掰开揉碎讲清楚,包括CA/B论坛的硬性限制、验证方式为什么比域名证书少、国内服务器前置合规检查怎么做、申请实操、部署翻车点,以及内网场景的自建CA替代方案。全程基于我实际测试和部署过的经验,遇到同类需求可以直接抄作业。
1. 为什么IP证书和域名证书完全不是一回事
1.1 CA/B论坛基线要求是硬门槛
先说一个很多人不知道的背景。浏览器厂商并不会无脑信任市面上所有证书,它们遵循的是CA/Browser Forum(简称CA/B论坛)制定的基线要求,英文叫Baseline Requirements。这个组织由全球主流CA机构和浏览器厂商共同组成,它规定了一系列硬性条款,CA机构不满足这些条款签发的证书,浏览器就不给信任。
其中有一条跟IP证书直接相关:公众信任的SSL/TLS证书,主题备用名称(Subject Alternative Name,SAN)里可以包含域名,也可以包含IP地址,但必须是公共IP地址。什么是公共IP?就是全球唯一、可路由的公网地址。像192.168.x.x、10.x.x.x、172.16.x.x这种私有网段,包括127.0.0.1回环地址、169.254.x.x链路本地地址,都属于保留地址或私有地址,公开CA一律不给签。
这一点是最容易被误解的地方。很多人跟我说“我内网服务器有IP地址,能不能申请个正规证书装上”,答案是:不能,因为私有IP不具备全球唯一性,CA无法验证这个IP真正属于你,签发出来也没法公信。内网场景有内网场景的解法,后面用第五章单独讲。
1.2 IP证书的验证方式被砍得只剩一条路
申请域名证书时,验证域名所有权的途径比较多,常见的有三种:
- 邮箱验证:CA向域名WHOIS邮箱或特定管理员邮箱发验证邮件。
- DNS验证:让你在域名解析记录里加一条TXT记录,CA去解析确认。
- HTTP文件验证:把CA给的验证文件放到网站根目录的指定路径下,CA通过HTTP访问确认。
IP证书没这么幸运。IP地址没有域名系统,没有WHOIS邮箱可以发邮件,也没有地方去添加TXT解析记录。唯一普遍可用的验证方式就是HTTP文件验证:CA给你一个随机文件名和一段随机内容,要求你在 http://你的IP:80/.well-known/pki-validation/xxx.txt 这个路径下放一个内容匹配的文件,CA通过公网访问该路径,确认拿到的内容一致,才认定你对这个IP有控制权。
这意味着什么?意味着申请IP证书有一个绝对前提:服务器的80端口必须对公网开放,而且CA必须能从外网访问到你。如果你的80端口被防火墙拦了、被安全组封了、或者运营商侧没有放行,证书验证这关基本过不去。
1.3 境内IP场景下的隐形前置条件
把场景拉回国内大陆。国内的主流云厂商对未备案的域名和未完成相关合规流程的Web服务,会有一套流量拦截策略,最直接的表现就是80和443端口对公网默认不放行,或者放行了也会被云厂商的拦截页面接管。
而IP证书的HTTP文件验证偏偏必须走80端口。两个条件一叠加,就形成了一个绕不开的链条:
- 要让80端口畅通,服务器必须完成对应合规流程;
- 合规流程里,网站备案又要求必须有一个有效域名;
- 很多用裸IP跑业务的团队恰恰没有域名。
这就是标题里“纯国内大陆内地认证”这个短语的真实含义,它不是指证书认证,而是指整个前置链路。很多人卡住,卡的不是证书签发本身,而是卡在了“没有域名就无法完成备案、无法完成备案就无法放行80端口、80端口不通就无法验证IP归属”这个死循环上。
所以如果你真的要在国内大陆的公网IP上部署一张正规CA签发的IP证书,第一步不是去选服务商,而是先确认你到底走哪条合规路径,这个下一章展开说。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 申请IP证书前必须完成的合规前置检查
2.1 主体身份认证:个人实名与企业认证的差异
不管是从国内服务商还是国际CA申请证书,第一步都是主体身份认证。DV级别证书(Domain Validation,域名型)通常只验证IP控制权,实名认证要求相对简单,个人实名即可。但OV级别证书(Organization Validation,企业型)或者EV级别,就需要提供企业营业执照、授权函、经办人身份信息等材料,CA工作人员还会做人工电话回访或工商信息核验。
国内企业申请IP证书的常见路径是:先在云厂商完成账号实名认证,个人认证或企业认证都行。企业认证需要上传营业执照,审核周期一般在几分钟到几个工作日。这里的建议是:如果证书将来要用于正式生产环境,尽量用企业主体申请,个人主体在证书信息里会暴露个人姓名,不够正式,而且后期如果需要升级OV证书还得重新走一遍企业认证流程。
2.2 80端口可见性检查:别等CA验证时才追悔
这一步必须前置。很多团队申请证书时火急火燎,付款、填单、提交,结果CA开始验证时发现80端口不通,来回折腾四五天。正确的做法是在提交申请之前,就模拟CA的验证请求自测一遍。
先在服务器上确认监听端口:
bash复制ss -lntp | grep :80
如果有服务监听80端口,再用本机curl测一下HTTP响应:
bash复制curl -I http://127.0.0.1/
本机通不代表公网通,还要在本地电脑或者一台外部服务器上测一下公网IP的80端口:
bash复制curl -I http://你的公网IP/
如果外部访问超时或者被拒绝,基本就是安全组或防火墙的问题。国内云服务器一般涉及三层检查:
- 云控制台的安全组规则,必须放行TCP 80端口;
- 服务器内部防火墙(firewalld、ufw、iptables),必须放行80端口;
- 云厂商侧是否有未备案拦截策略,这个需要去控制台确认。
第三点尤其重要。如果服务器绑定的域名没有备案,云厂商会在HTTP层做拦截,你从外网curl大概率看到一个拦截提示页,这样CA的验证请求同样会被拦截。所以,80端口不是“能通就行”,而是“返回的内容必须是CA指定的验证内容”。
2.3 域名与备案的取舍:正规公网业务绕不开的一步
现在必须把话讲透:国内大陆地区的公网Web服务,对外的80、443端口流量是要求完成ICP备案的,而各地的备案系统目前基本要求网站信息里至少填写一个域名。也就是说,业务可以跑在IP上,但备案这关需要有一个域名作为门户。
这对于想申请IP证书的团队来说,实际可走的合规路径有两条:
第一条,注册一个域名,完成ICP备案,然后再去申请IP证书。这种场景下证书是签给IP的,SAN里写的是IP,跟域名可以没关系;但备案的完成让80端口得以放行,IP归属验证才能顺利进行。证书部署后,用户用 https://IP 访问是可以成功的,浏览器认可证书,因为证书SAN里绑定了该IP。
第二条,如果你的业务明确不对外公网开放,只是在办公室、园区、机房内部跑,那根本不需要申请公开CA的证书,直接用自建CA方案,第五章会讲具体操作。这种情况不需要备案,因为不构成对公网的网站服务。
这里还要提醒一点,备案期间80端口通常是被拦截的,所以申请IP证书要卡在备案完成后再操作。整个备案周期根据各省通管局的速度,快的话一周,慢的话三四周,项目排期要预留这个时间。
3. IP证书申请实操:从选服务商到验证文件配置
3.1 服务商选型与支持范围
先列一个对比表,方便大家直接看结论。
| 渠道 | IP证书支持情况 | 验证方式 | 适合场景 |
|---|---|---|---|
| 阿里云/腾讯云等平台免费证书 | 不支持,免费证书仅限域名 | 无 | 纯域名网站 |
| 阿里云/腾讯云等平台品牌证书 | 部分支持,需联系客户经理申请 | HTTP文件 | 已完成备案的大中型企业 |
| 国际CA(DigiCert/Sectigo/GlobalSign等) | 支持,通过国内代理商申请 | HTTP文件 | 跨国业务、外企、对兼容性要求高的场景 |
| 国内专注数字证书的服务商(如JoySSL等) | 支持,流程相对本地化 | HTTP文件 | 国内服务器、需要中文支持和发票的个人/企业 |
| 自建CA | 完全支持,自签发 | 自己验 | 内网/私有化部署 |
个人建议是:如果服务器在国内、又有备案条件,优先选国内支持IP证书的服务商,因为无论是工单沟通、验证流程指导还是售后,语言和时区都没有障碍。如果业务涉及海外客户,或者对浏览器信任根要求特别严格,可以选国际CA的IP证书。免费证书这边确实没有IP选项,这是行业现状,不用浪费时间找了。
3.2 提交申请时的关键字段
选好服务商后,在证书订单页面一般需要填写以下关键信息:
- IP地址:填写你要签发证书的公网IP,必须是公网可路由地址;
- 证书类型:DV(域名验证型)或OV(企业验证型),多数IP证书场景选DV足够;
- 算法:推荐RSA 2048,个别场景需要ECC 256也可以,但兼容性上RSA更稳;
- 有效期:常见1年,有些服务商提供90天短期证书,按需选即可。
提交后,服务商会生成一条验证记录,给出一个类似下面的验证文件路径:
text复制http://你的公网IP/.well-known/pki-validation/1a2b3c4d5e6f7g8h9i0j.txt
文件内容是一串固定字符串,比如:
text复制20240415010203040506070809
注意,这个路径是固定的,文件名不要改,内容也不要增加任何空格、换行或引号。很多人在这里栽跟头,一会儿多打一个换行,一会儿不小心保存成带BOM头的UTF-8,都会导致验证失败。
3.3 放置验证文件时最容易忽略的权限问题
验证文件通常要放在网站的根目录下,也就是 /.well-known/pki-validation/ 这个子目录里。如果你的服务器上没有现成的Web服务,临时起一个也行,但要对所有外网IP开放访问。
以Nginx为例,假设你的站点根目录是 /var/www/html:
bash复制sudo mkdir -p /var/www/html/.well-known/pki-validation/
sudo echo "20240415010203040506070809" > /var/www/html/.well-known/pki-validation/1a2b3c4d5e6f7g8h9i0j.txt
sudo chmod 644 /var/www/html/.well-known/pki-validation/1a2b3c4d5e6f7g8h9i0j.txt
然后确认Nginx配置没有对该路径做额外拦截。有些团队在Nginx里配置了全局的location规则,比如:
nginx复制location / {
deny all;
}
或者对 .txt 后缀做了特殊处理,那验证请求就会被403挡掉。验证前一定要从外网完整测一遍:
bash复制curl http://你的公网IP/.well-known/pki-validation/1a2b3c4d5e6f7g8h9i0j.txt
返回结果必须和你写入的内容完全一致,包括结尾没有多余的空行。
这里还要注意一个坑:如果服务器上配置了HTTPS强制跳转,比如把80端口的请求全部301到443,部分CA的验证程序会跟随跳转,但有些不会。稳妥的做法是验证期间暂时关闭80到443的跳转,让验证文件在80端口直接以200状态返回,等签发完成后再恢复跳转。
3.4 签发成功后的文件下载
验证通过后,CA一般会在几个小时内签发证书。服务商控制台会提供一个压缩包,里面包含不同服务器的格式:Nginx、Apache、IIS、Tomcat、Java KeyStore等。下载时主要关注Nginx目录下的两个文件:.crt 证书文件和 .key 私钥文件。有些服务商还会额外提供一个 ca-bundle 文件,里面是中间证书链,部署时需要拼接,这个细节放到第四章讲。
4. 部署阶段最容易翻车的三个细节
4.1 Nginx配置IP证书的正确姿势
拿到证书文件后,常规的Nginx配置长这样:
nginx复制server {
listen 443 ssl;
server_name 123.45.67.89;
ssl_certificate /etc/nginx/ssl/123.45.67.89_fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/123.45.67.89.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
有几个细节要注意。server_name 直接写IP地址即可,Nginx 1.19.4以上版本不会对纯IP的server_name报错,旧版本会有警告但不影响运行。证书文件推荐用 _fullchain.crt 这种包含完整链的文件,也就是服务器证书加上中间证书拼在一起的完整链,后面会解释为什么。
配置完成后测试并重载:
bash复制nginx -t
systemctl reload nginx
如果之前80端口有业务,别把它关了,因为证书续期验证可能还要再走一次80端口HTTP验证。
4.2 证书链不完整导致的“公开信任”危机
很多人部署完证书后用Chrome一打开,哗,地址栏还是红的,报错信息是“证书链不完整”或者“无法验证服务器证书的签发者”。这类问题90%以上是证书链拼接错了。
浏览器信任证书是一个链条逻辑:服务器证书 -> 中间证书 -> 根证书。根证书在浏览器内置的信任库里,服务器只需要下发中间证书给客户端,让客户端能串联到根证书即可。如果你只配置了服务器证书,没有把中间证书一起下发,客户端找不到中间证书,就无法构建完整信任链。
解决办法是下载证书文件时,优先选择 fullchain.crt 这种已经拼接好的文件。如果是手动拼接,顺序是:服务器证书放最上面,紧接着放中间证书,最后是根证书。顺序颠倒也会导致Nginx启动失败或者客户端校验失败。
拼接命令示例:
bash复制cat 123.45.67.89.crt ca-bundle.crt > fullchain.crt
然后可以用下面的命令验证证书链:
bash复制openssl s_client -connect 123.45.67.89:443 -servername 123.45.67.89
看返回里是否包含“SSL handshake has read X bytes”和完整证书链。这一步一定要做。
4.3 CVE-2005-4900与弱哈希算法的检查与修复
这个话题被热搜词单独提到了,确实是个实际会碰到的排查项。CVE-2005-4900描述的是旧版证书或旧系统里仍在使用SHA-1弱哈希签名算法的问题。现代浏览器早就把SHA-1签名的证书视为不安全,甚至直接拒绝信任。
判断一个证书是否还在用SHA-1,用OpenSSL看:
bash复制openssl x509 -in server.crt -text -noout | grep -A1 "Signature Algorithm"
正常情况应该看到的是 sha256WithRSAEncryption 或 sha384WithRSAEncryption 这类现代算法。如果出现 sha1WithRSAEncryption,说明这个证书文件是老的,或者服务商签发异常,必须重新申请。
更容易被忽略的是运维时间差问题:有些老服务器上签发了好几张证书,比如之前的旧证书散落在各种目录里,新证书部署后,某个服务路径还是指向旧证书,结果扫描软件一扫,报出一堆旧证书的SHA-1漏洞。这种情况不是新证书的问题,而是服务器上残留了旧证书文件,需要全盘排查清理。
另外,如果客户端设备过于老旧,比如老旧的Windows XP、SP3之前的系统,或者老固件的工控设备,它们不支持TLS 1.2以上协议,也不支持SHA-256签名,这类设备访问新证书会直接失败。遇到这种环境,要么升级设备固件或系统,要么在设计上把这些设备迁移到内网组网,用私有CA去兼容,而不是纠结公开证书的算法问题。
5. 公网IP证书搞不定时:内网场景的替代方案
5.1 为什么私有IP无法申请公开证书
回到最初的问题。如果你的服务器IP是 192.168.1.10、10.0.0.5 这种私有地址,或者是你自己在VMware里搭的虚拟网络,那无论找任何公开CA都签不了。原因前面说过:私有IP不是全球唯一的,CA没法通过公网来验证你对这个IP的控制权,而且就算签了,浏览器也不会信任CA去签发一个私有地址证书,因为这相当于CA认证了一个可以被任何人在任何地方重复使用的地址。
这就是内网场景最现实的痛点:内部系统要上HTTPS,不能用公开证书,但又不想每次打开浏览器都看到一个刺眼的红色警告页。
5.2 OpenSSL自建CA并签发带IP SAN的证书
我的建议是自建一套内部CA,用OpenSSL就能实现,成本为零。大致流程分三步。
第一步,生成CA根证书私钥和自签名根证书:
bash复制mkdir -p ~/myca
cd ~/myca
# 生成CA私钥
openssl genrsa -out ca.key 2048
# 生成自签名根证书,有效期10年
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt \
-subj "/C=CN/CN=My Internal CA"
第二步,签发服务器证书,关键是要带上IP的SAN扩展:
bash复制# 生成服务器私钥
openssl genrsa -out server.key 2048
# 生成证书签名请求
openssl req -new -key server.key -out server.csr \
-subj "/C=CN/CN=192.168.1.10"
# 创建扩展配置文件,指定IP为SAN
cat > ext.cnf <<EOF
subjectAltName = IP:192.168.1.10
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
EOF
# 用CA签发服务器证书
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt -days 825 -sha256 -extfile ext.cnf
注意,ext.cnf 里的 subjectAltName 可以直接写IP,不需要加域名前缀,这是IP证书自签和自建CA里最容易忽略的一步。如果不加SAN,Chrome等现代浏览器会提示“证书与该IP不匹配”。
第三步,给Nginx配置这组自签证书:
nginx复制server {
listen 443 ssl;
server_name 192.168.1.10;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
...
}
5.3 客户端信任根证书的分发与维护
自建CA签发的证书,浏览器默认不信任,因为根证书 ca.crt 不在系统信任库里。所以最重要的一步是:把 ca.crt 安装到所有需要访问该服务的终端上。
Windows下的安装方式:双击 ca.crt,选择“安装证书”,存储位置选“本地计算机”,然后手动选择“受信任的根证书颁发机构”目录,完成安装后重启浏览器。
macOS下:双击证书导入钥匙串访问,在证书列表里把信任策略改成“始终信任”。
Linux下:把 ca.crt 放到 /usr/local/share/ca-certificates/,然后执行 update-ca-certificates。
移动端稍微麻烦点。Android系统要看版本和品牌,Android 7以上系统的App默认不信任用户安装的CA证书,除非App在代码里配置了信任用户凭据;iOS则在“设置-通用-关于本机-证书信任设置”里开启信任开关。这一点在内部办公系统上影响很大,很多App团队会把内部CA证书内置到App的res/raw目录下,写代码信任指定CA,而不是依赖系统信任库。
另外要规划好证书的生命周期。自建CA签发的服务器证书建议有效期设短一点,比如825天左右,到期后重新签发;根证书则设10年,尽量少动。如果根证书泄露或者被误删,所有终端都要重新安装新根证书,维护成本是几何级上升的,所以CA私钥一定要妥善保管。
6. 我实测踩过的坑和排查思路
6.1 验证文件能打开但CA一直说验证失败
第一张IP证书申请的时候,我在验证环节卡了整整一天。用浏览器打开验证文件路径,内容完全正确,复制粘贴也是对的,但CA后台一直显示“验证未通过”。后来我用服务商给的验证工具反复试,才想起来去查一下服务器的访问日志。
不看不知道,一看才发现Nginx的access log里,CA验证程序的User-Agent实际上是被我配置的一条 location / { deny all; } 规则拦截的。浏览器访问时带了Cookie,走了另外的路径或者直接返回了200,但CA爬虫不带任何Cookie,命中deny规则直接403。而我手动curl测的是本机访问,走的是loopback,没有经过公网进站的链路,所以没测出问题。
这个坑给我的教训是:验证文件测试要模拟最严格的访问场景,用外网IP、无Cookie、无Referer的裸请求去测,而不是用浏览器本地打开。输出状态码必须是200,不能是301、302跳转,更不能是403。
6.2 Android老版本设备提示证书不受信任
第二个坑发生在证书部署完成后的兼容性测试阶段。Windows和iOS的浏览器都正常,结果拿一台老旧的Android 6设备一访问,直接报“证书不受信任”。
排查过程是这样的:先用手机浏览器访问,发现报错;再用电脑OpenSSL检查证书链,发现 openssl s_client 返回的证书链顺序是正确的,服务器证书也带了中间证书。理论上不该有问题。
最后用Wireshark抓包看TLS握手,发现问题出在Android 6设备自带的CA信任库版本太老,它不包含我这张证书所用的中间CA的交叉信任锚点。这个属于老设备固件问题,无解。
最后的处理方案是:在Nginx里额外下发一张跨签名证书(Cross-Signed Certificate),把旧设备的信任链桥接到新证书上。但更省事的做法是服务商在签发时提供兼容旧设备的证书格式。如果你遇到类似情况,先确认你的证书签发CA是否支持交叉签名,如果不支持,那只能接受老设备无法访问,或者引导用户升级系统。内网场景下用自签CA反而没这个问题,因为根证书是我们自己下发到终端的。
6.3 续期时验证文件被清理导致“空窗期”
IP证书的有效期通常只有一年,但续期比首次申请更容易翻车。我遇到过的情况是:证书到期前一个月,邮件提醒我去续期,结果打开后台发现验证文件路径404了。原因是我在证书签发成功后,把 /var/www/html/.well-known/ 目录整个清理掉了,觉得不会再有用。
问题来了,续期时CA会要求你重新放置一个新的验证文件,验证文件和首次申请的路径结构一样,只是内容和文件名变了。目录没了,验证就失败。
后续我把验证目录的保留写进了证书续期的SOP,同时加了脚本监控:每个月检查 /.well-known/pki-validation/ 目录是否存在,不存在就自动重建。另外,如果你的业务系统本身不依赖于80端口,只是为证书验证保留一个纯静态目录,务必保证这个目录不随着业务版本更新被覆盖。
如果你用的是ACME类自动化工具(比如acme.sh、certbot),IP证书的续期也可以自动化,但前提是服务商和CA支持ACME协议签发IP证书。目前支持ACME签IP的CA和国内服务商并不多,多数还是走手动放置验证文件的流程,所以人工续期时更要把验证目录当成基础设施来维护,而不是一次性的临时文件。
最后分享一个我个人的体会。IP证书这个需求,表面上是证书签发问题,实际上牵涉到合规链路、端口策略、证书链维护、老设备兼容性甚至续期流程的自动化。你问我要不要为了省事跳过备案直接用别的方式放行80端口,我的态度很明确:正规业务不要走灰色路线,老老实实注册域名完成备案,再走证书申请流程,虽然流程长一点,但后期几乎不会再有变数。
如果是纯内网系统,也别瞎折腾公开CA了,直接自建CA,装根证书到各终端,白天照常工作,晚上不用提心吊胆。这套方案我自己在多个项目里跑了两三年,稳定可靠。希望这篇记录能帮你少走几步弯路。
