自签名证书、私有 CA 与内网 HTTPS:如何搭建可信的本地开发环境

为什么本地开发也需要 HTTPS

很多开发者习惯在本地使用 http://localhost 进行开发,认为 HTTPS 只有在生产环境才需要。但现代 Web 平台的能力正在快速向安全上下文(Secure Context)收拢:Service Worker、Web Crypto API、Geolocation、摄像头与麦克风访问、HTTP/2 与 HTTP/3、Cookie 的 Secure 属性等,都要求页面运行在 HTTPS 之下。即便你在本地绕过这些限制,代码一旦部署到测试环境或生产环境,协议差异带来的行为不一致往往会制造难以排查的 bug。

更现实的问题是内网服务。公司内部的 API 网关、微服务、管理后台、IoT 设备控制面板,如果只跑 HTTP,任何能接入内网的人都可以明文嗅探甚至篡改流量。搭建一套自己的私有 CA,为内网域名签发证书,不仅能解决信任问题,还能让整个团队共用同一套可信根证书。

自签名证书、私有 CA 与公共 CA 的区别

理解三者差异是选型的前提。

自签名证书(Self-Signed Certificate) 是指证书的签发者(Issuer)和使用者(Subject)是同一个实体,证书由自己的私钥签名。浏览器和操作系统默认不信任它,因为没有任何受信任的第三方为其背书。它适合个人临时测试,但不适合团队协作——每个人都要单独手动信任。

私有 CA(Private CA) 则模拟了公共 CA 的层级结构:你创建一个根证书(Root CA),用它签发服务器证书(Leaf Certificate)。只要把根证书安装到客户端信任库,所有由它签发的证书都会被自动信任。这是团队和内网环境的正确做法。

公共 CA(如 Let's Encrypt、DigiCert)签发的证书被所有设备默认信任,但无法为 .local.internal 或纯内网 IP 签发证书,且证书透明度日志(CT Log)会公开你的域名。内网服务通常不希望暴露在公共日志中。

方案一:mkcert —— 最省事的本地开发方案

如果你只是想让 localhost 和几个本地域名跑上 HTTPS,mkcert 是最佳选择。它在本地创建一个 CA,自动将其安装到系统信任库,并为你签发证书。

# 安装(macOS)
brew install mkcert
brew install nss   # 如果使用 Firefox

# 创建本地 CA 并安装到系统信任库
mkcert -install

# 为本地域名签发证书
mkcert localhost 127.0.0.1 ::1 dev.example.test "*.dev.example.test"

生成的 localhost+4.pemlocalhost+4-key.pem 即可配置到 Nginx、Caddy 或 Node.js 服务中。mkcert 的 CA 根证书位于 $(mkcert -CAROOT) 目录,把它分发给团队成员并安装,就能实现团队内的互信。

mkcert 的局限在于它面向开发场景,不提供服务端自动化、证书吊销等能力,不适合作为长期的内网 CA 基础设施。

方案二:用 OpenSSL 搭建私有 CA

当需要为整个内网签发证书时,手动用 OpenSSL 建立 CA 更可控。以下是最小可用流程。

1. 生成根 CA

# 生成根私钥(4096 位,AES256 加密)
openssl genrsa -aes256 -out ca.key 4096

# 生成自签名根证书,有效期 10 年
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
  -subj "/C=CN/O=MyCompany/CN=MyCompany Internal Root CA" \
  -out ca.crt

ca.key 是整条信任链的命脉,必须离线保存、严格限制访问权限(chmod 400),理想情况下放在硬件令牌或离线机器上。

2. 为服务器签发证书

# 生成服务器私钥和 CSR
openssl genrsa -out server.key 2048
openssl req -new -key server.key \
  -subj "/C=CN/O=MyCompany/CN=api.internal.example.com" \
  -out server.csr

# 使用扩展文件指定 SAN(现代浏览器不再看 CN,只看 SAN)
cat > san.cnf <<EOF
subjectAltName = DNS:api.internal.example.com, DNS:*.internal.example.com, IP:10.0.0.10
extendedKeyUsage = serverAuth
EOF

# 用根 CA 签发,有效期 825 天
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -out server.crt -days 825 -sha256 -extfile san.cnf

关键点:必须包含 SAN 扩展。Chrome 自 58 版本起完全忽略 Common Name,只认 subjectAltName。同时 extendedKeyUsage = serverAuth 明确证书用途,避免被误用。

3. 分发根证书

ca.crt 分发给所有需要访问内网服务的客户端:

  • macOS:双击导入「钥匙串访问」,在「系统」钥匙串中设为「始终信任」。
  • Windowscertutil -addstore -f "ROOT" ca.crt 或通过组策略批量推送。
  • Linux:复制到 /usr/local/share/ca-certificates/,执行 update-ca-certificates
  • iOS/Android:通过 MDM 或手动安装描述文件,注意 Android 7+ 后 App 默认不信任用户证书,需要在 network_security_config.xml 中显式声明。
  • Firefox:使用独立信任库,需在「证书管理器」中单独导入。

方案三:现代工具链

手动 OpenSSL 流程繁琐且容易出错,生产级内网 CA 可以考虑这些工具:

  • step-ca(Smallstep):提供 ACME 协议支持,可自动签发和轮换证书,内置 OCSP 和吊销列表,适合作为长期内网 CA。
  • CFSSL(Cloudflare):JSON 驱动的证书签发工具,适合批量管理。
  • Vault PKI Secrets Engine:HashiCorp Vault 的 PKI 引擎,支持动态短期证书,与身份系统集成,适合有合规要求的企业。
  • Caddy:内置 tls internal 指令,自动生成并管理本地 CA,配置反向代理时几乎零成本。

常见坑与最佳实践

证书有效期:公共 CA 已将有效期压缩到 398 天,内网证书虽可更长,但建议不超过 825 天,并建立轮换机制。

私钥保护:根 CA 私钥绝不能放在 Web 服务器上。签发服务器证书时应使用中间 CA(Intermediate CA),根 CA 保持离线。

通配符与 SAN*.internal.example.com 不匹配 internal.example.com 本身,需要同时列入 SAN。通配符也不匹配多级子域。

HSTS 慎用:本地开发环境不要开启 HSTS,否则一旦证书出问题,浏览器会强制 HTTPS 且无法绕过,清理起来非常麻烦。

吊销机制:私有 CA 也应提供 CRL 或 OCSP,至少维护一份吊销列表,证书泄露时能及时止损。

结语

本地和内网 HTTPS 不是可选项,而是现代开发的基线要求。个人开发用 mkcert 几分钟即可搞定;团队内网则应建立正式的私有 CA,配合 step-ca 或 Vault 实现自动化签发与轮换。核心原则只有两条:根私钥离线保护,根证书全员分发。做到这两点,你就能拥有一个既安全又无需忍受浏览器警告的开发环境。

未经允许不得转载:任鹏个人博客 » 自签名证书、私有 CA 与内网 HTTPS:如何搭建可信的本地开发环境

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏