如何修复 Git SSL 证书问题
每当 Git 通过 HTTPS 执行 clone、fetch 或 push 且无法验证服务器的 SSL/TLS 证书时,就会出现 Git SSL 证书问题。完整提示通常是 fatal: unable to access 'https://...': SSL certificate problem: unable to get local issuer certificate;对于内部服务器则是 SSL certificate problem: self signed certificate。Git 会中止操作,因为它无法从服务器证书回溯构建一条到达其所信任根证书颁发机构(CA)的信任链。
与浏览器不同,Git 并不总是自带信任库。在 Linux 上它依赖系统 CA 证书包;在 Windows 上则可能使用操作系统证书库(Schannel)或一个内置 CA 文件,具体取决于安装方式。当该信任库缺失、过时,或恰好缺少签发服务器证书的那个 CA 时,Git 就无法验证服务器从而拒绝连接。令人放心的是,几乎所有情况都对应六个根本原因之一,而修复往往只需一条命令。本指南按"能解决最多情况"的顺序依次讲解。
常见原因
在进入分步修复之前,下表汇总了 Git SSL 证书问题背后的六个根本原因、各自来源以及你遇到它时的典型场景。你可以据此快速判断哪一步适用于你的情况。
| 根本原因 | 来源 | 典型场景 |
|---|---|---|
| CA 证书包过时 | 客户端 | 系统 ca-certificates 包比签发服务器证书的 CA 更旧。 |
| 自签名证书 | 服务器 | 内部 Git 服务器使用了任何信任库都不认识的自签名证书。 |
| 系统日期/时间不正确 | 客户端 | 错误的时钟让有效证书看起来已过期或尚未生效。 |
| 企业代理 / MITM 防火墙 | 网络 | 代理用自己的 CA 重新签名 HTTPS 流量,而 Git 不信任该 CA。 |
| 缺少企业根 CA | 客户端 | 公司 CA 尚未被添加到 Git 的 CA 证书包中。 |
| 证书链不完整 | 服务器 | 服务器只发送了叶证书,缺少中间证书。 |
步骤 1:检查系统日期和时间
TLS 信任模型依赖于将证书的有效期与当前时间进行比较。如果你的机器时钟有误 — 哪怕只差几个小时,或年份设错 — Git 也可能把一张完全有效的证书当作已过期或"尚未生效",从而报 SSL 证书问题。这是排查成本最低的原因,因此从这里开始。
检查当前时间并确认已启用 NTP 同步。在 Linux 上使用 timedatectl;在 macOS 上使用 sntp time.apple.com;在 Windows 上使用 w32tm 命令。若显示时间有偏差,更正后再重试 Git 命令。
# Linux:查看状态并启用 NTP 同步
timedatectl
sudo timedatectl set-ntp true
# macOS:对照 Apple 时间服务器检查偏差
sntp time.apple.com
# Windows:强制与已配置的时间服务器重新同步
w32tm /resync
更正时钟后,重新执行失败的 git clone 或 git pull。如果错误消失,说明问题仅仅是日期不正确。这一步在虚拟机或容器从挂起状态恢复后尤为重要,因为客户机时钟可能已大幅漂移。
步骤 2:更新 CA 证书(update-ca-certificates)
如果你的 CA 证书包比签发服务器证书的 CA 更旧,Git 就无从信任它。这在公共 CA 轮换根证书后很常见 — 例如旧的 DST Root CA X3 过期后,Let's Encrypt 迁移到 ISRG Root X1 和 X2。更新系统 CA 包会用所有新近加入的根 CA 刷新证书包。
在 Debian、Ubuntu 及衍生版上,安装 ca-certificates 包并运行 update-ca-certificates。在 RHEL、CentOS、Fedora 及衍生版上,对应的工具是 update-ca-trust。在 macOS 上,通过 Homebrew 安装 ca-certificates,让 Git 自带的 curl 能找到最新的证书包。
# Debian / Ubuntu
sudo apt update
sudo apt install -y ca-certificates
sudo update-ca-certificates
# RHEL / CentOS / Fedora
sudo dnf install -y ca-certificates
sudo update-ca-trust
# macOS(Homebrew)
brew install curl ca-certificates
如果输出显示新增了证书,那么缺失的根几乎可以肯定是原因所在。重试 Git 操作。如果仍然失败,下一步是显式地把 Git 指向刷新后的证书包。
步骤 3:配置 Git 的 http.sslCAInfo
Git 的 http.sslCAInfo 设置指向 Git 用于验证的 CA 证书包文件。如果未设置且 Git 无法自动检测到证书包,或它指向一个过时/空文件,验证就会以"unable to get local issuer certificate"失败。先查看当前值,再将其指向系统证书包。
# 打印 Git 当前使用的 CA 证书包(可能为空)
git config --global http.sslCAInfo
# 将 Git 指向 Debian/Ubuntu 系统证书包
git config --global http.sslCAInfo /etc/ssl/certs/ca-certificates.crt
# RHEL/Fedora 使用单一合并证书包
git config --global http.sslCAInfo /etc/pki/tls/certs/ca-bundle.crt
为确认 Git 现在收到的是完整且有效的证书链,用 openssl s_client 检查证书。下面的命令会打印链中的每一张证书;你应至少看到服务器(叶)证书和为其签名的中间证书。
# 连接并打印完整证书链
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
# 显示有效期、颁发者、使用者及 SAN
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates -issuer -subject -ext subjectAltName
如果证书链完整且证书包路径正确,Git 此时应能成功验证。在 Windows 上,另一个办法是将 Git 的 SSL 后端切换为 schannel,它会使用操作系统信任库而非 PEM 文件。
步骤 4:临时禁用 SSL 验证
当你急需从已知的内部服务器克隆时,可以为单条命令禁用验证。这是一种有用的诊断手段 — 如果关闭验证后克隆成功,你就确认了问题纯粹是信任问题,而非网络或身份验证问题。但是,切勿对公共仓库长期关闭验证,因为它会消除对中间人攻击的全部防护。
# 仅对单次克隆禁用(不改变全局配置)
git -c http.sslVerify=false clone https://example.com/repo.git
# 全局禁用(不推荐;测试后请移除)
git config --global http.sslVerify false
# 重新启用验证
git config --global --unset http.sslVerify
优先使用 -c 形式,它只作用于这一次调用且不影响全局配置。一旦确认原因是信任问题,就应通过步骤 6 正确修复(添加证书),而不是依赖禁用验证。
步骤 5:检查代理设置
许多企业网络运行着会执行 HTTPS 检查的代理或防火墙。它会拦截 TLS 连接,用公司自有的 CA 重新签名流量再转发出去。Git 不信任该 CA,因此握手失败并报 SSL 证书错误,表现与缺少 CA 包一模一样。线索通常是:该错误只发生在公司网络上。
首先检查是否设置了代理环境变量,以及 Git 是否配置了代理。然后,若确实存在 HTTPS 检查,获取企业 CA 证书并按步骤 6 的方法将其加入证书包。
# 查看代理相关的环境变量
env | grep -i proxy
# 让 Git 使用 HTTP(S) 代理
git config --global http.proxy http://proxy.corp.example.com:8080
git config --global https.proxy http://proxy.corp.example.com:8080
# 移除代理设置
git config --global --unset http.proxy
git config --global --unset https.proxy
如果禁用代理后 Git 恢复正常,那么代理或其 CA 就是原因。在 Windows 上,还应检查 Internet 选项(运行 inetcpl.cpl)中的系统代理,以及许多工具都会读取的 HTTP_PROXY/HTTPS_PROXY 环境变量。
步骤 6:修复自签名证书问题
内部 Git 服务器常使用自签名证书,或由公共信任库都不包含的内部 CA 签发的证书。正确的修复方式是将该证书添加到系统 CA 包,使 Git 信任它 — 这样既保持验证开启,又保护了你的流量。不要仅为绕过一个内部服务器而全局禁用验证。
用 openssl 导出服务器证书,然后将其安装为受信任的 CA。在 Debian/Ubuntu 上,将 PEM 文件放入 /usr/local/share/ca-certificates/ 并以 .crt 为扩展名,再运行 update-ca-certificates;在 RHEL/Fedora 上,将其复制到 /etc/pki/ca-trust/source/anchors/ 并运行 update-ca-trust。
# 导出内部服务器的证书
echo | openssl s_client -connect git.internal.example.com:443 \
-servername git.internal.example.com 2>/dev/null \
| openssl x509 -out git-server.crt
# Debian / Ubuntu — 全系统信任
sudo cp git-server.crt /usr/local/share/ca-certificates/git-server.crt
sudo update-ca-certificates
# RHEL / Fedora — 全系统信任
sudo cp git-server.crt /etc/pki/ca-trust/source/anchors/git-server.crt
sudo update-ca-trust extract
安装证书后,在仍开启验证的情况下重试 Git 操作。如果成功,你就修复了根本原因而非掩盖它。在 Windows 上,最干净的做法是把证书添加到"受信任的根证书颁发机构"库,Git 的 schannel 后端随后会自动使用它。
Git SSL 配置命令
下面的代码块汇总了诊断和修复 SSL 证书问题时最常用的 Git 与 OpenSSL 命令。当尚未明确该用哪一步时,把它放在手边备用。
# 查看所有与 SSL 相关的 Git 配置
git config --global --get-regexp ssl
# 设置 Git 用于验证的 CA 证书包
git config --global http.sslCAInfo /etc/ssl/certs/ca-certificates.crt
# Windows:使用操作系统信任库而非 PEM 文件
git config --global http.sslBackend schannel
# 仅对一次克隆禁用验证(仅测试用)
git -c http.sslVerify=false clone https://example.com/repo.git
# 检查服务器呈现的证书链
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
# 打印有效期、颁发者、使用者及 SAN
echo | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -dates -issuer -subject -ext subjectAltName
# 统计服务器发送了多少张证书(证书链完整性)
echo | openssl s_client -connect example.com:443 -showcerts 2>/dev/null \
| grep -c "BEGIN CERTIFICATE"
快速参考:原因与修复
| 症状 / 线索 | 根本原因 | 修复方法 |
|---|---|---|
| "unable to get local issuer certificate" | CA 证书包缺失或过时 | 更新 CA(步骤 2);设置 http.sslCAInfo(步骤 3) |
| "self signed certificate" | 内部服务器证书不受信任 | 将证书加入证书包(步骤 6) |
| 其他机器正常,仅某台报错 | 时钟有误或 CA 过时 | 检查时间(步骤 1);更新 CA(步骤 2) |
| 仅在公司网络上报错 | 代理 / MITM 防火墙 | 配置代理并添加其 CA(步骤 5) |
| 证书续期后报错 | 新的签发 CA 不在证书包中 | 更新 CA(步骤 2) |
| 需要快速克隆一次 | 信任尚未配置 | 对单条命令临时禁用(步骤 4) |
常见问题
"SSL certificate problem: unable to get local issuer certificate" 是什么意思?
它表示 Git 收到了服务器证书,但在其受信任的 CA 包中找不到签发该证书的证书颁发机构("颁发者"),因而无法构建信任链。原因几乎总是 CA 包过时、服务器缺少中间证书,或会重新签名流量的企业代理。请更新 CA 证书并将 http.sslCAInfo 指向刷新后的证书包;若仍存在,用 openssl s_client -showcerts 检查证书链。
把 http.sslVerify 设为 false 安全吗?
仅作为临时诊断、且仅对单条命令使用 git -c http.sslVerify=false clone ... 才安全。全局设置会关闭每一次 HTTPS Git 操作的证书验证,这意味着中间人攻击者可以在你毫无察觉的情况下截获你的凭据和仓库内容。始终优先通过添加正确的证书(步骤 6)来修复,而不是禁用验证。
如何在 Windows 上修复 Git SSL 证书问题?
最简单的 Windows 修复方法是把 Git 的 SSL 后端切换到操作系统信任库:git config --global http.sslBackend schannel。Git 随后会使用 Windows 证书库,你可以用 certmgr.msc 管理它。如果你偏好 OpenSSL 后端,可从 curl 项目下载最新的 cacert.pem 并将 http.sslCAInfo 指向它。对于内部服务器,请把证书添加到"受信任的根证书颁发机构",Schannel 即会自动信任它。
为什么 Git 在终端里失败,但同一网址在浏览器里正常?
浏览器维护着自己频繁更新的信任库,且通常会自动下载缺失的中间证书,而 Git 依赖系统 CA 包或内置 PEM 文件,后者可能更旧或不完整。浏览器也可能配置了不同的代理路径。修复方法相同:更新 CA 证书,将 http.sslCAInfo 指向正确的证书包,并用 openssl s_client 确认证书链。若涉及企业代理,也要把代理的 CA 加入证书包。
总结
Git SSL 证书问题看似棘手,但归根结底只关乎一个问题:Git 能否找到一条从服务器证书回溯到其所知根 CA 的受信任路径?请按顺序执行这六个修复步骤:更正系统时钟、更新 CA 证书、将 http.sslCAInfo 指向正确的证书包、用 openssl s_client 验证证书链、检查企业代理,并将任何自签名或内部证书加入信任库。绝大多数情况由步骤 1 至步骤 3 即可解决。
Git 恢复连接后,请通过定期系统更新保持 CA 包最新,避免长期关闭 http.sslVerify,并将内部服务器证书视为只需安装一次、之后持续信任的配置项。少量的主动维护即可让你的 clone、fetch 和 push 在 HTTPS 上安全顺畅地运行。