ARTICLE DETAIL

资讯详情

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

内网HTTPS证书申请与部署全攻略:从自建CA到Nginx/Tomcat

内网HTTPS证书申请与部署全攻略:从自建CA到Nginx/Tomcat 前阵子帮朋友在内网搭了一套知识库系统域名、服务都配好了结果打开浏览器一看地址栏里那个“不安全”的红色警告特别扎眼还有用户反馈说登录密码总觉得不踏实。这让我想起一个被问过很多次的问题内网环境到底怎么申请SSL证书才能让HTTPS正常访问今天就把这个事从头到尾捋一遍从原理到实操从自签名到私有CA从Nginx到Tomcat把坑都给你填平。不管你是运维、开发还是自己搭NAS折腾着玩这篇文章都值得收藏。很多人一听“内网环境”就觉得无所谓反正外面访问不到HTTP明文也凑合。实际情况远没那么简单尤其是现在各种应用、agent智能体搭建、内部系统对接越来越多内网HTTPS已经成了刚需。接下来我会先讲清楚内网证书和公网证书的差异再给三条可落地的申请路径然后带上完整的部署配置示例最后把续期、过期监控和排查技巧一起整理到位。1. 内网HTTPS到底难在哪先搞清楚要解决的问题1.1 为什么内网也需要HTTPS内网不等于安全。这个观念如果不扭转后面很多事情都会踩坑。内网的流量虽然不出公网但局域网里的路由器、交换机、无线AP甚至公司里的上网行为管理设备都有可能看到传输的内容。如果你用的是HTTP明文那么账号密码、接口返回值、业务数据在链路上就是“裸奔”的稍微懂点抓包的人都能直接读出来。另一个更现实的痛点是浏览器。现在Chrome、Edge、Firefox对新版本都默认把HTTP站点标记为“不安全”在地址栏直接给你一个灰蒙蒙的警告。尤其是一些内部系统需要调用摄像头、麦克风等能力时HTTPS是硬性要求HTTP下浏览器根本不会授权。如果你在开发内网agent或AI应用很多Web API也要求安全上下文同样是HTTPS先行。还有混合内容问题。即使你的主页面用了HTTPS如果页面里还引用了一些HTTP的图片、脚本、样式浏览器会直接拦截这些子资源表现出来的现象就是页面样式错乱、功能按钮没反应、接口调不通。排查到最后你会发现根子就在于某个静态资源是HTTP的。所以从根上把整站切到HTTPS是省心而不是折腾。1.2 HTTP和HTTPS的本质区别HTTP和HTTPS的区别本质上就是多了一层TLS/SSL协议。TLS在做的事情可以简单拆成三件身份验证、数据加密、完整性校验。身份验证靠的就是证书服务器把证书发给客户端客户端检查证书是不是可信CA签发的、域名是否匹配、是否过期加密靠的是密钥协商客户端和服务器在握手阶段生成会话密钥之后所有内容都用对称加密传输完整性校验则是用MAC/哈希算法确保数据没有被中间人篡改。用生活化的话说HTTP就是把明信片直接投递到对方手里路上每个人都能看到内容HTTPS则是把内容装进带锁的保险箱而且锁的钥匙只有通信双方有快递员、中转站、仓库管理员都打不开。证书在其中扮演的角色就是保险箱上那个“厂家认证标签”告诉你这把锁确实来自可信的厂家而不是骗子贴上去的假标签。之所以要强调证书的信任链是因为加密本身并不能防止中间人攻击。如果客户端不去验证证书的合法性攻击者完全可以自己伪造一个证书然后跟客户端重新建立加密通道所有数据照样被窃听。这也是为什么自签名证书虽然能加密浏览器却一个劲地报错——它不信任你这个签发者。1.3 内网证书和公网证书的核心差异公网SSL证书是由CA机构如DigiCert、GlobalSign、Lets Encrypt等签发的申请时一般需要证明你对该域名拥有控制权。验证方式有域名验证、组织验证和扩展验证级别越高越严格。拿到证书后浏览器、操作系统默认信任这些CA所以部署完就能直接看到小锁。内网证书就不一样了。内网通常用的是IP地址或者自定义的内部域名比如192.168.1.10或者wiki.local。这类地址和域名在公网没有注册记录CA机构无法帮你验证所有权也没法给你签发匹配的证书。这时候你有三条路一是用公网证书玩“擦边”二是自建私有CA三是用自动化工具配合DNS验证。另外内网证书的有效期管理也和公网不一样。公网免费证书通常90天有的国内云厂商提供一年免费版自建CA签发的证书想签多久签多久但太长的有效期会带来更大的泄露风险最好还是控制在2-5年以内同时做好过期监控。不要以为内网证书没人管就能一直用过期之后浏览器照样报错到时候你都不知道是哪里出的问题。2. 申请SSL证书的几种可行路径2.1 路径一从公网免费证书“借”到内网用如果你内网的服务恰好绑定了一个真实的公网域名而且这个域名可以临时解析到你控制的一台服务器上做验证那么可以直接申请公网免费证书再把证书下载回来部署到内网机器。这个方法尤其适合那些“内外网同域名”的部署场景申请一次两边通吃。国内用得比较多的就是阿里云SSL证书免费版操作路径大致是登录证书服务控制台选择“免费证书”创建证书、填写绑定域名然后按提示完成域名验证。审核通过后就能下载证书里面会包含xxx.pem和xxx.key两个文件分别是证书链和私钥。这套流程对新手很友好控制台点一点就行不需要自己写CSR证书格式也是现成的。但你要特别注意阿里云免费证书目前的趋势是有效期缩短、续期要手动操作。以前免费证书有一年期现在不少批次是90天有效期到期后需要重新申请签发再替换到服务器上。所以我在团队里一直强调免费证书不是“一劳永逸”必须建立续期提醒。更稳妥的做法是给证书到期时间建一张表或者写个脚本定期检查别等到服务突然报错才反应过来。2.2 路径二自建私有CA给内网设备统一发证如果内网系统比较多或者你不想依赖外部的云厂商那自建私有CA是最理性的一条路。核心思路是自己生成一个根证书然后由这个根证书给内网各个服务签发子证书。只要客户端信任了你的根证书它就会自动信任你签发的所有服务器证书相当于你自己开了一家“CA公司”内网所有证书都由你统一管理。自建CA的好处很明显不受公网域名限制IP地址、内网域名、通配符域名都能签证书数量不受限内部系统再多也无所谓私钥掌握在自己手里数据不出一台机器。缺点是需要自己维护好根证书的私钥一旦泄露整个内网的信任体系就崩了所以根证书私钥一定要离线保管签发证书时才临时拿出来。用OpenSSL自建CA的具体过程我会在下一部分详细展开。这里先给出整体步骤生成根私钥和自签名根证书、创建CA目录结构、用根证书签发服务器证书、将根证书分发到所有客户端并导入系统信任区。整个过程看起来有点繁琐但实际操作一次之后就会觉得比想象中简单而且后续给新服务发证非常快。2.3 路径三内网ACME自动化签发与续期说到证书管理ACME协议是绕不开的关键词。Lets Encrypt就是靠ACME协议自动签发和续期证书的你只要装个Certbot配置一下Nginx插件剩下的事情它全包了。但这里有个大前提ACME验证需要让CA能够访问到你或者至少在DNS层面完成你拥有域名的验证。纯内网环境既没有公网入口也没有外部DNS记录直接使用公共CA的ACME是走不通的。如果你愿意把内网域名在公网DNS做个TXT记录做验证那也可以用ACME工具申请一张匹配的证书再手动部署到内网。但这样做相对绕还要依赖公网解析不如私有CA干净。还有一些方案是内网搭建自己的ACME服务比如用Smallstep的step-ca它既能自建CA又支持ACME协议让内网客户端像用Lets Encrypt一样自动续期适合比较成熟的内部基建团队去尝试。对于大多数中小团队和家庭用户我的建议是开发环境用mkcert一键生成本地信任的证书测试环境用私有CA签发的证书生产环境如果内外网域名一致就直接用云厂商免费证书如果不一致就私有CA一把梭。别一上来就追求复杂自动化先把信任机制跑通后面再逐步加监控和自动化。方案适用场景优点缺点公网免费证书有真实域名且可验证浏览器默认信任申请简单有效期短需手动续期内网IP无法申请自建私有CA大量内网IP/域名长期使用完全自控可签任意域名和IP需要分发根证书有一定维护成本内网ACME私有CA团队内设备很多追求自动化自动签发、自动续期、信任集中搭建门槛高需要额外服务维护3. 核心实操从申请到部署的完整流程3.1 生成私钥和CSR参数选错后面全是坑不管走哪条证书申请路径发证之前都是先产生密钥对和证书签名请求CSR。用OpenSSL生成私钥命令本身很简单但参数选错后面全是坑。最常见的两个坑一是密钥长度太短二是没有添加SAN扩展。先说密钥长度。目前RSA推荐至少2048位生产环境建议直接上4096位。密钥长度直接影响握手性能和安全性但也不是越长越好4096位在CPU较弱的设备上确实会增加握手耗时。如果你用的是现代Nginx、Tomcat更推荐使用ECC密钥例如prime256v1或secp384r1曲线同等安全级别下密钥更短、握手更快生成命令和RSA略有不同。再说SAN扩展这是新手特别容易忽略的。以前证书验证只要看CNCommon Name字段现在主流浏览器都要求证书里带有Subject Alternative NameSAN否则域名不匹配照样报错。使用OpenSSL生成CSR时可以通过-addext subjectAltNameDNS:wiki.local,IP:192.168.1.10,IP:127.0.0.1来添加多个域名和IP地址非常方便。如果你不写这一行后面证书可能只认第一个域名其他域名访问时就会出现“不安全”提示。生成私钥和CSR的参考命令如下# 生成RSA 2048位私钥 openssl genrsa -out server.key 2048 # 生成带SAN扩展的CSR openssl req -new -key server.key -out server.csr \ -subj /CCN/STBeijing/LBeijing/OMyCompany/OUIT/CNwiki.local \ -addext subjectAltNameDNS:wiki.local,DNS:wiki.internal,IP:192.168.1.10如果是自建CA签发那么现在就可以拿着这个CSR去签发证书具体命令我会在3.2节里给。如果是向云厂商申请那你需要把server.csr的内容复制到申请页面里云厂商基于这个CSR生成证书到时候下载的私钥就没有了因为私钥一直留在你自己手上。3.2 证书格式转换cer、pem、pfx、jks和Tomcat那点事证书申请下来之后你可能会拿到各种后缀的文件pem、cer、crt、pfx、p12、jks看着眼花缭乱。其实核心就两类一类是Base64编码的文本格式常见后缀有pem、crt、cer另一类是二进制或带密码保护的PKCS#12格式常见后缀有pfx、p12以及Java生态里的JKS。不同Web服务器对格式的要求不同搞清楚放哪个文件就行。最常用的是Nginx它直接用PEM格式的证书文件和私钥文件一个server.crt一个server.key搞定。但如果你用的是Tomcat或者中间件是基于Java的那就经常需要把证书转成PKCS12格式也就是pfx或p12。这也就是很多人在网上搜“cer转tomcat ssl证书pfx”的原因场景非常典型。从PEM转PFX的命令是openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt -passout pass:YourPassword如果从云厂商下载的证书是cer后缀其实内容和crt是一样的都是PEM编码你可以直接用上面的命令转换不需要特殊处理。转换后拿到server.pfx就可以配合keytool导入到Tomcat或Java Keystore中使用。比如# 将PFX导入到JKSJava Keystore keytool -importkeystore \ -srckeystore server.pfx -srcstoretype PKCS12 -srcstorepass YourPassword \ -destkeystore server.jks -deststoretype JKS -deststorepass YourPassword这个过程非常容易踩坑常见问题是keytool版本不一致导致JKS和PKCS12转换报错以及密码不对导致密钥库恢复不了。建议所有密码统一管理环境变量里放一份文档里放一份别到时候连自己都忘了。3.3 Nginx配置HTTPS最常用的部署姿势Nginx是内网反向代理的首选配置HTTPS大概是所有操作里最顺手的。假设你的证书文件在/etc/nginx/ssl/server.crt私钥在/etc/nginx/ssl/server.key那么在server块里加监听和证书路径就可以。一个基础的配置片段如下server { listen 443 ssl; server_name wiki.local; 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; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; 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 { listen 80; server_name wiki.local; return 301 https://$host$request_uri; }这里有几个细节值得展开说。第一ssl_protocols只保留TLSv1.2和TLSv1.3TLSv1.0和TLSv1.1安全性太差不建议再开。第二ssl_ciphers不要用默认值建议使用HIGH:!aNULL:!MD5这类保守组合避免被扫描到弱加密套件。第三如果内网客户端里有老版本的Windows 7自带浏览器它们可能不支持TLSv1.3你需要根据实际情况兼容TLSv1.2这是内网环境特有的老旧客户端问题。配置完成后执行nginx -t检查语法然后nginx -s reload重载。如果服务跑在80端口别忘了外面再套一层HTTP跳转这样用户访问HTTP地址时会自动跳到HTTPS体验更好。跳转配置我已经写在上面了直接抄就行但如果你有多级代理要注意X-Forwarded-Proto头要正确传递否则后端应用看到的还是HTTP可能还会生成一堆HTTP链接。3.4 Tomcat配置HTTPS别被443端口绕晕Tomcat是Java生态里非常常见的Web容器配置HTTPS的思路和Nginx不太一样。Tomcat不直接读PEM证书而是倾向于使用PKCS12或JKS格式的密钥库。上一节我们已经把证书转成了server.pfx这里接着配置。把server.pfx拷贝到Tomcat的conf目录下然后编辑conf/server.xml找到被注释掉的Connector配置改成类似下面的内容Connector port8443 protocolorg.apache.coyote.http11.Http11NioProtocol SSLEnabledtrue schemehttps securetrue keystoreFileconf/server.pfx keystoreTypePKCS12 keystorePassYourPassword clientAuthfalse sslProtocolTLS /这里最容易被绕晕的地方是端口。Tomcat本身的SSL连接器一般跑在8443端口你需要在前面再套一个Nginx监听443然后把请求转发到Tomcat的8443。或者你也可以直接改Tomcat的端口为443但那样需要root权限绑定低端口不如让Nginx统一处理外部流量Tomcat只负责内部服务。很多人在Tomcat里配置了HTTPS后发现始终无法访问先确认端口是否在防火墙规则内再确认keystorePass是不是和生成PFX时设置的密码一致。还有一个隐蔽的坑keystoreFile如果是相对路径它是以Tomcat的CATALINA_BASE为基准的不是当前目录。如果你把PFX放在conf目录下配成conf/server.pfx一般没问题但如果你把PFX放到别的目录绝对路径更省事。3.5 让内网客户端信任你的证书证书部署到服务器只是第一步客户端也得信任才行。用公网免费证书的话系统默认信任不需要额外操作。但如果是自建私有CA签发的证书就需要把根证书安装到每台客户端的系统信任区里否则浏览器还是会警告。Windows系统导入根证书的操作很简单双击根证书文件选择“安装证书”存储位置选“本地计算机”然后“将所有的证书都放入下列存储”点击“浏览”选择“受信任的根证书颁发机构”一路下一步即可。macOS则是打开“钥匙串访问”把证书拖进去然后在“信任”选项里把SSL设为“始终信任”。如果有多台机器可以用组策略或MDM统一下发不用一台一台手动装。还要提醒一句如果你网内有Windows、Linux、Android、iOS多平台设备根证书的信任策略各不相同尤其是Android和iOS对根证书的管理越来越严。比如iOS会要求安装描述文件并手动开启“证书完全信任”开关安卓在新版本里也需要到“加密与凭据”里安装CA证书。如果你只在一台电脑上测通了别以为全公司都能用真的得每种客户端都验证一遍。4. 证书续期、过期监控与常见问题排查4.1 过期时间怎么看一行openssl命令搞定证书过期是内网HTTPS问题里最高发的一个没有之一。尤其是自建CA签的证书经常签个三五年就忘了到期当天浏览器突然报警用户投诉一堆。最直接的排查方法就是用OpenSSL查看证书有效期# 查看PEM证书的过期时间 openssl x509 -enddate -noout -in /etc/nginx/ssl/server.crt # 查看二进制DER格式证书的过期时间 openssl x509 -enddate -noout -inform DER -in server.cer # 查看远程服务器的证书过期时间 echo | openssl s_client -connect 192.168.1.10:443 2/dev/null | openssl x509 -noout -enddate第一条命令非常适合排查本机证书第三条命令适合排查别的服务器特别是在内网里你怀疑某台机器证书过期的时候不用登录它就能直接看到结果。如果你需要批量检查多台服务器可以写个简单的Shell循环把IP列表放进去逐个输出到期时间。再配合定时任务每个月跑一次就不会出现大规模证书过期的尴尬。4.2 阿里云免费证书的续期套路如果你的内网服务用的是阿里云免费证书现在一定要把“免费续期”这四个字看清楚。免费证书不等于自动续期大部分情况需要在控制台重新申请一张新证书等签发后再次下载、上传到服务器然后重启Nginx或Tomcat。这个过程不是自动的而且证书到期时间一到旧证书立即失效如果你的替换流程没有提前完成就会有一段空窗期。我的建议是给证书做一个“提前14天提醒”的机制不一定要用多复杂的系统一个Shell脚本加上Cron就能搞定。每天检查一下server.crt的到期天数如果少于14天就发邮件或钉钉通知。脚本很简单核心就是上面写的openssl x509命令配合date计算天数差。千万别高估自己的记忆免费证书一多靠人脑管理肯定要漏。另外云证书的密钥格式和自建OpenSSL生成的格式可能会略有差异但下载下来的私钥文件一般就是PEM格式直接替换到Nginx配置里即可。如果你用的是Tomcat需要重新进行格式转换和导入步骤所以每次续期后都要把整个配置链路从头到尾走一遍也正因如此我建议核心服务尽量用自动化脚本把“证书文件替换”这个动作固定下来减少人工出错。4.3 部署后的经典报错与排查方法部署完HTTPS后遇到报错太正常了我列几个高频场景你可以按图索骥。第一类浏览器报“NET::ERR_CERT_AUTHORITY_INVALID”。这说明客户端不信任签发证书的CA。如果你是自建CA那要先确认根证书有没有安装到客户端如果是用IP访问还要确认证书的SAN里有没有包含这个IP。第二类报“证书链不完整”或“无法完整验证”。这是服务器只发送了站点证书没有把中级CA证书一并发送。解决办法是生成一个包含站点证书和CA证书的chain文件Nginx的ssl_certificate可以指向这个合并文件。合并就是用cat拼接cat server.crt ca.crt server-chain.crt第三类页面能打开但样式乱了控制台报mixed-content错误。这表示页面里有HTTP资源被浏览器拦截。你需要全局搜索代码里的http://把静态资源改成https://或使用协议相对地址//。如果内网应用比较老这一步经常要花不少时间建议直接从源头上让应用强制输出HTTPS链接比如在Nginx里加add_header Content-Security-Policy upgrade-insecure-requests能自动把HTTP子请求升级成HTTPS。还有一类是端口和服务本身的问题比如Nginx没监听443、防火墙没放行、Tomcat的8443没开。这类问题用curl -vI https://192.168.1.10和telnet 192.168.1.10 443来排查能很快定位是网络不通还是证书有问题。4.4 测试和日常维护的一些额外建议对于内网HTTPS日常测试工具有几个可以分享。JMeter在做接口压测或录制脚本时如果要录制HTTPS流量需要先让你本机信任服务器证书或者在JMeter里配置HTTP客户端实现接受证书。这不算特别难但很多人第一次录制时会被证书验证卡住忽略了JMeter自身并不使用系统证书库而是使用Java的cacerts。最简单的方式是把你的根证书导入到JDK的cacerts里命令是keytool -import -alias myrootca -keystore $JAVA_HOME/lib/security/cacerts -file rootCA.crt默认密码是changeit导入后重启JMeter即可。另一个实用工具是Wireshark它可以抓取HTTPS明文流量前提是你把私有CA根证书和服务器私钥配置好。在Wireshark的TLS协议设置里导入你的根证书或会话密钥就能解密流量这比盯着密文猜来猜去强太多了。不过要强调的是这本事只能用来调试自己的系统别拿去做不该做的事。日常维护上我还会定期检查系统日志和证书文件权限。私钥文件只允许root或运行用户读取权限设置为600。如果被其他用户拿到私钥等于整个加密通道都是透明的。另外如果你在公司环境里用了vsftpd这类FTP服务它也支持显式的TLS/SSL。此时证书的格式要求和Nginx类似直接把server.crt和server.key配置到vsftpd的rsa_cert_file和rsa_private_key_file即可。注意vsftpd对证书链的支持比较有限如果证书链有问题客户端连接时可能反复要求输密码这个和Web服务器的排查套路不太一样要稍微留意。5. 最后几点经验走了这么长的路最后分享几点实在的体会。内网HTTPS这件事最怕的就是“临时用一下”的心态。用自签名证书一分钟就能加密但每个浏览器都要手动点信任每台机器都要点一遍最后浪费的时间反而更多。如果你要管理超过三五台设备直接自建私有CA是投入产出比最高的方案虽然前期要花半小时搭建后面一发证书一分钟搞定。我自己踩过最深的坑就是自建CA根证书私钥没有离线保管。有一次为了图方便把根私钥直接留在CA签发机器上结果那台机器中了勒索病毒虽然没有直接影响签发但吓得我立刻重建了一整套CA所有客户端的根证书都要换一遍那个“酸爽”我现在都记得。所以再强调一次根证书私钥一定要离线、脱离服务器环境签发时才临时导入签完立刻移除。还有一个实用的小习惯所有的证书我都按“项目名域名到期日期”来命名比如wiki-wiki.local-20261231.crt证书文件名里直接写到期时间一眼就能知道什么时候需要续期。配合一个简单的定时扫描脚本基本上不会出现证书过期到用户先发现的情况。把这个方法分享给你希望你的内网HTTPS之路少一些坑多一些从容。
返回列表