ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

内网HTTPS证书申请与部署全攻略:自签名、私有CA与公共CA实践

内网HTTPS证书申请与部署全攻略:自签名、私有CA与公共CA实践 做内网项目的HTTPS改造时最让人纠结的往往不是Nginx配置也不是Tomcat那堆参数而是“证书到底从哪来”。外网服务器一条certbot就能签下Lets Encrypt证书内网机器没有公网域名、没有入站流量部门内部系统要加密访问却总是卡在申请SSL证书这一步。这篇文章我打算把内网环境下常见的申请与落地方式从头梳理一遍涵盖自签名、私有CA、公共CA免费证书三条路线并给出Nginx、Tomcat等场景的直接配置参考。如果你正在为内网系统“上HTTPS”发愁或者想搞清楚为什么浏览器总提示不安全这篇内容应该能帮你把思路捋顺。1. 内网HTTPS为什么难难在哪1.1 内网系统上HTTPS的真实价值很多人觉得内网系统不暴露在公网何必多此一举上HTTPS这个想法在过去还算成立现在越来越站不住脚。内网不等于安全办公Wi-Fi可以被伪造交换机端口可能被接入不明设备内网抓包工具随手就能用。明文HTTP下账号密码、接口参数、业务数据在网络里都是裸奔的只要有人在内网链路做一次抓包隐私和敏感信息就全部暴露了。另一个理由是移动端强制要求。现在很多内网系统要配企业微信、钉钉、小程序或者自研App这些平台在Android和iOS上对HTTP接口限制非常严格明文流量容易直接被系统拦截或弹安全警告。如果你不想被这些平台的安全策略反复折腾尽早把内网服务接入HTTPS是更省事的选择。再加上现在很多企业做等保、做内部安全审计网络流量加密是明文写进要求的。内网服务不加密审计的时候很难解释。所以内网环境下把HTTPS落实好不是“锦上添花”而是规范化管理里绕不开的一步。1.2 证书在HTTPS握手中的作用以及两大拦路虎要搞定内网HTTPS得先理解证书在HTTPS里干什么。简单说浏览器和服务器建立加密连接之前服务器需要出示一张数字证书里面包含站点身份信息、公钥、有效期以及签发该证书的CA证书颁发机构。浏览器收到证书后会依次检查三件事证书链是否符合系统信任的CA、域名是否和证书中的SANSubject Alternative Name匹配、证书是否在有效期内。全部通过才会继续建立加密会话。这也是内网HTTPS最常见的两大拦路虎。第一个是“证书不受信任”自签名证书或者私有CA签发的证书不在浏览器内置信任列表里浏览器就直接报NET::ERR_CERT_AUTHORITY_INVALID。第二个是“域名和证书不匹配”很多人给IP做了证书用户却通过主机名访问或者证书里只写了Common Name没写SAN浏览器照样不认。搞明白这两点之后内网SSL证书的申请思路就清晰了要么想办法让客户端信任我们自己的CA要么申请一张证书让证书里的信息能覆盖用户实际访问的域名或IP。1.3 内网申请SSL证书的三条路线选择针对不同场景内网环境下申请SSL证书主要有三条路线路线信任效果部署复杂度典型场景维护成本自签名证书单机可信其他机器需手动导入低临时调试、个人测试每台机器都要导入私有CA统一签发导入根证书后全网可信中等公司内部正式系统、多台服务器需维护CA根证书公共CA免费证书浏览器原生信任中等有公网可解析域名的内网服务证书有效期短需定期续期公共CA证书在浏览器里最“权威”但正因为权威申请门槛也高。CA需要验证域名所有权而内网域名比如gitlab.internal.local在公网DNS无法解析CA验证根本过不去。纯IP地址的证书很多公共CA也不再签发。所以很多内网环境最后都选择了私有CA或自签名路线既能完全掌控证书签发又不受公网域名限制。2. 不同证书申请方式的完整实操2.1 自签名证书一条命令生成但要带SAN如果你只是临时给某个内部服务开HTTPS自签名是最快的方式。直接用openssl生成一张带SAN的证书openssl req -x509 -newkey rsa:2048 -sha256 -nodes \ -days 825 \ -keyout server.key \ -out server.crt \ -subj /CN192.168.1.50 \ -addext subjectAltNameIP:192.168.1.50,DNS:dev.internal.example.com解释一下关键参数-x509表示直接生成自签名证书而不是CSR-nodes表示私钥不加密这样Nginx启动时不用频繁输密码-days 825约等于两年多一点这是考虑到部分系统对超过825天的新证书会有兼容性提示内网环境也建议别把有效期拉太长-addext subjectAltName...把IP和域名都写进SAN这一步非常关键很多人生成证书后浏览器报域名不匹配多半就是漏了SAN。生成之后服务端配置用server.key和server.crt但其他机器访问时浏览器仍会提示不安全。要让浏览器信任需要把server.crt导入到访问端系统的“受信任的根证书颁发机构”。Windows下双击证书文件选择“安装证书”存储位置选“本地计算机”然后手动放到“受信任的根证书颁发机构”LinuxCentOS/RHEL系执行cp server.crt /etc/pki/ca-trust/source/anchors/ update-ca-trustUbuntu/Debian系则是放到/usr/local/share/ca-certificates/后执行update-ca-certificates。自签名的坑在于证书分发。测试环境只有一两台机器还好一旦客户端数量上来每台机器手动导入一遍很崩溃而且换证书时又得重来。所以自签名只建议应急或测试用。2.2 mkcert内网开发神器三分钟让本机信任如果你主要是在本机或少量开发环境调试mkcert比手动openssl省心很多。它本质上是帮你自动生成一个本地CA根证书并安装到系统信任库然后用这个CA去签发目标域名/IP的证书整个过程命令化操作# 安装mkcert后先初始化本地CA mkcert -install # 为指定域名和IP签发证书 mkcert 192.168.1.50 dev.internal.example.com执行完当前目录会生成192.168.1.501.pem和192.168.1.501-key.pem直接丢给Nginx用即可。mkcert -install把本地CA根证书装进了系统信任区所以本机浏览器访问时不会再报错。如果其他同事也要访问只需要把mkcert生成的根证书默认路径在~/Library/Application Support/mkcert/或~/.local/share/mkcert/下可以找到分发给他们导入系统信任区即可。mkcert非常轻量适合开发联调场景但它生成的CA严格说只在你的团队内部有效公司正式系统不建议长期依赖。2.3 自建私有CA给整个内网统一签发证书当内网机器多起来自签名就熬不住了。此时应该自建一个私有CA用它统一给所有服务器签发证书。大家只要信任一次根证书之后由这个根CA签发的任何服务器证书都会被浏览器认可。这个思路和公共CA完全一致只不过信任的根换成了你自己。第一步生成CA根证书openssl genrsa -out ca.key 4096 openssl req -x509 -new -key ca.key -days 3650 -out ca.crt -subj /CNCompany Internal CAca.key是CA私钥必须妥善保管最好离线备份加密存储。ca.crt是根证书将来要分发到所有客户端。第二步为某台服务器生成私钥和证书签名请求CSRopenssl genrsa -out gitlab.key 2048 openssl req -new -key gitlab.key -out gitlab.csr \ -subj /CNgitlab.internal.local \ -addext subjectAltNameDNS:gitlab.internal.local,IP:10.0.0.5第三步用CA签发服务器证书openssl x509 -req -in gitlab.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -out gitlab.crt -days 825 -sha256 \ -extfile (printf subjectAltNameDNS:gitlab.internal.local,IP:10.0.0.5)签发后gitlab.crt就是服务器证书gitlab.key是私钥ca.crt用于给客户端做信任验证。注意签发时同样要写SAN否则部署后域名/IP校验依然会失败。第四步把ca.crt分发到所有访问端。这一步最费人工企业环境如果有AD域可以通过组策略批量下发没有域控的小团队最少也要写一份文档告诉大家如何导入。分发范围越广内网后续上HTTPS就越顺畅。2.4 申请公共CA免费证书阿里云/Lets Encrypt可行但有限制再来说说公共CA免费证书。很多人盯着阿里云、腾讯云的免费SSL证书或者在考虑Lets Encrypt这些方案在内网环境能不能用能但有前置条件证书里的域名必须能在公网被解析验证否则CA核验域名所有权时根本找不到目标。以阿里云免费证书为例操作路径大致是控制台搜索“数字证书管理服务”进入SSL证书申请页面选择一个免费证书规格填写你要保护的主域名比如gitlab.example.com验证方式选择DNS。如果域名托管在阿里云DNS通常可以一键自动添加TXT解析记录审核几分钟到几小时不等签发后按需下载Nginx、Tomcat格式的证书文件。这里有个容易误解的点证书绑定的域名在公网需要能解析并不代表流量必须真的走公网。你完全可以把example.com的A记录解析到一条测试公网IP或者不对外提供业务然后企业内部DNS把gitlab.example.com解析到内网服务器IP 10.0.0.5。客户端在内网访问gitlab.example.com时实际流量完全走内网但浏览器校验证书时证书里写的域名和访问域名一致就能通过。这种方式既拿到了浏览器原生信任的证书又保住了内网低延迟访问。Lets Encrypt也同理。用acme.sh配合DNS API比如阿里云DNScurl https://get.acme.sh | sh -s emailyouexample.com acme.sh --issue --dns dns_ali -d gitlab.example.com前提依然是这个域名在公网DNS有权威解析并且你持有云解析API密钥。纯离线内网、完全不能访问外网的环境这条路线走不通只能回头用私有CA。还要强调一点公共CA现在基本不签纯IP证书免费证书尤其如此。所以如果你只有一个内网IP没有域名别指望阿里云免费证书能帮你老老实实走自签名或私有CA。2.5 证书续期与到期检查公共CA证书有效期普遍短Lets Encrypt是90天阿里云个人免费证书现在也缩短到了3个月左右必须把续期当成周期任务。常见的检查命令# 查看证书过期时间 openssl x509 -enddate -noout -in server.crt # 查看证书内容 openssl x509 -in server.crt -text -noout如果用了acme.sh自动签发的Lets Encrypt证书可以开启自动续期acme.sh --install-cert -d gitlab.example.com \ --key-file /etc/nginx/ssl/gitlab.key \ --fullchain-file /etc/nginx/ssl/gitlab.crt \ --reloadcmd nginx -s reload脚本会自动在到期前续期并重载Nginx。私有CA签发的证书有效期通常可以设得更长但依然要有到期检查机制建议写个简单脚本扫描证书目录配合crontab每周执行到期前30天邮件或企业微信告警。3. 在内网常见服务上部署HTTPS3.1 Nginx部署HTTPS证书链别拼错拿到证书之后配置Nginx其实是最简单的。这里给一个可以直接用的server块server { listen 443 ssl; http2 on; server_name gitlab.internal.local; ssl_certificate /etc/nginx/ssl/gitlab.crt; ssl_certificate_key /etc/nginx/ssl/gitlab.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...; }如果你是申请公共CA证书下载的压缩包里面往往有server.crt和ca.crt需要把两个文件拼接成fullchaincat server.crt ca.crt fullchain.crt配置里就用fullchain.crt。证书链不完整会导致部分客户端校验失败这是Nginx部署里非常容易踩的坑。还有一点提醒内网环境下不要把HTTP到HTTPS的跳转做得太绝对。如果有服务暂时还没上证书强制跳转会直接断掉。稳妥做法是先让所有域名都覆盖证书再考虑统一跳转。3.2 Tomcat部署HTTPScer转pfx再上Tomcat部署HTTPS比Nginx绕一些因为Tomcat默认偏好PKCS12/JKS格式的密钥库而很多渠道下发的证书是PEM/CRT格式。热搜词里“cer 转 tomcat ssl 证书 pfx”就是典型的现实需求。假设你手里有server.crt、server.key和ca-chain.pem转成PFX的完整命令# 如果cer是DER格式先转成PEM openssl x509 -inform DER -in server.cer -out server.pem # 把PEM证书和私钥打包为PFXcertfile可以附加中间证书 openssl pkcs12 -export \ -in server.pem \ -inkey server.key \ -out server.pfx \ -name tomcat \ -certfile ca-chain.pem转换时会要求设置PFX的导出密码这个密码要记住一会配置里要用。然后把server.pfx丢到Tomcat的conf目录修改conf/server.xmlConnector port8443 protocolorg.apache.coyote.http11.Http11NioProtocol maxThreads150 SSLEnabledtrue SSLHostConfig Certificate certificateKeystoreFileconf/server.pfx certificateKeystorePassword你的密码 typeRSA/ /SSLHostConfig /Connector注意Tomcat 8.5使用SSLHostConfig嵌套结构老教程里直接用keystoreFile属性的写法在新版本已经逐渐弃用。PFX的好处是私钥和证书在一个文件里Tomcat读起来省事你要真想用JKS也行但PFX通用性更好跨平台迁移也方便。3.3 网关统一终止HTTPS与Spring Boot内置配置如果内网服务是微服务架构不建议每个业务服务都自己配证书。更好的做法是在Nginx网关或负载入口统一终止HTTPS后面到业务服务走内网HTTP降低证书维护面和私钥暴露风险。这也是我实际项目里推荐的做法证书统一由网关管理业务方无感知。当然单机应用不需要网关分层时Spring Boot直接内置HTTPS也很方便。把证书和私钥转成PKCS12格式后配置server: port: 8443 ssl: enabled: true key-store: classpath:server.pfx key-store-type: PKCS12 key-store-password: yourpassword这种方式适合内部小工具或管理系统部署简单不需要额外装Nginx。但微服务和前后端分离项目统一网关始终是更可控的方案。4. 内网HTTPS的常见问题排查4.1 浏览器报“不安全”的排查顺序浏览器提示不安全原因千奇百怪但绝大多数逃不开下面几类。建议按照顺序排查证书链是否完整。用命令查openssl s_client -connect gitlab.internal.local:443 -showcerts openssl s_client -connect gitlab.internal.local:443 -servername gitlab.internal.local看输出里是否有完整的“Certificate chain”如果只有一层或缺少中间证书就是fullchain没拼对。根证书是否已经导入访问端。私有CA签发的证书客户端必须信任对应的根证书公共CA一般没这个问题但如果你是从某台内网机器复制了不全的链也会报authority invalid。证书域名和用户访问的地址是否匹配。证书SAN里写了gitlab.internal.local用户却用https://10.0.0.5访问必然报错。要么给IP补一张带IP SAN的证书要么让客户端走域名访问。证书是否在有效期内以及系统时间是否准确。内网机器时间漂移非常常见机器时间比证书有效期差了几天甚至几年浏览器直接判证书无效。先date看时间再openssl x509 -dates看证书有效期对比一下。私钥和证书是否匹配。如果部署时搞混了多套证书Nginx可能启动失败Tomcat可能报错用这条命令核对openssl x509 -noout -modulus -in server.crt | openssl md5 openssl rsa -noout -modulus -in server.key | openssl md5两个md5一致才说明匹配。4.2 访问IP和证书域名不一致的处理内网环境很多用户习惯直接输IP访问但证书签发时域名是主体。处理方式有两条路一是给证书SAN里同时加上IP地址像前面openssl命令里-addext subjectAltNameIP:10.0.0.5,DNS:gitlab.internal.local那样二是统一约定域名访问通过内网DNS解析或客户端hosts映射把域名指到对应IP。从长期维护看我更推荐统一用域名。因为IP会变证书里IP多了以后迁移麻烦而域名只是一个解析记录改映射比换证书成本低太多。如果公司有内网DNS优先把内部服务域名规划起来。4.3 老客户端打不开HTTPS页面的兼容问题内网环境经常藏着Windows 7、老版本浏览器、旧版JDK客户端。这些老客户端对TLS 1.3甚至TLS 1.2的支持都不好而你如果把ssl_protocols只配了TLSv1.2和TLSv1.3老客户端可能直接握手失败。这里有个取舍问题安全要求上TLS 1.0和1.1早该停用但内网存量设备太多时直接停用会影响业务。我的做法是默认配置TLSv1.2和TLSv1.3特殊老设备单独开一个兼容端口跑TLS 1.0/1.1并限定访问来源。这样既保证绝大多数流量走强加密又不至于把老设备一棍子打死。4.4 内网证书管理最容易忽略的五个坑我总结一下这几年在内网摸爬滚打遇到的高频问题每条都踩过不止一次系统时间不同步。内网机器不做NTP校时证书明明是刚签的浏览器却提示过期或“证书无效”。给内网所有服务器和客户端统一配置好NTP同步这个坑能避开一大半。根证书只装服务器不装客户端。私有CA方案里根证书必须下发到访问端不是只放在签发服务器上就行。私钥权限不设防。server.key一旦泄露等于HTTPS裸奔。务必把私钥设为仅root/管理员可读不要放进代码仓库。证书链缺中间证书。证书链不完整在PC浏览器上有时不报错但App、Java、curl、微信小程序里经常校验失败。多端测试很重要。只图方便不写SAN。新证书签发时忘记加SAN最后部署完发现浏览器依然提示域名不匹配只能重新签发。另外如果内网有Java程序调用HTTPS接口遇到PKIX path building failed也很常见本质是JVM没有信任你的私有CA或证书链不完整。要么把根证书导入JAVA_HOME/lib/security/cacerts要么在代码里自定义信任库。这个点很多测试同学第一次遇到时特别懵。最后再分享一个我自己的习惯折腾完这些我现在的流程基本固定下来了开发环境用mkcert几十秒出证书本机全信任测试和正式内网环境直接搭一个私有CA根证书通过公司文档库统一分发之后所有内网服务都用它签发只有遇到“必须让公网生态比如小程序回调地址用HTTPS”的需求才去申请公共CA免费证书并且用acme.sh这类工具做好自动续期。每次签完证书我都会立刻把证书文件备份到一个固定目录同时记录域名、IP、到期时间、私钥路径。内网证书一旦多了光靠脑子记住每张证书什么时候到期早晚要出问题。如果你也在做内网HTTPS改造我建议先别急着部署花十分钟想清楚“信任链”怎么建立再动手操作后面能省下大量排查时间。
返回列表