shuguang's blog

环境决定基础,选择决定价值,努力决定方向。

解决使用代理后 Git 无法获取本地证书问题

  • 解决使用代理后 Git 无法获取本地证书问题。

#解决使用代理后 Git 无法获取本地证书问题

  • 问题背景

    • 使用代理访问 Github 后,出现SSL certificate problem: unable to get local issuer certificate的错误。

  • 原理解析

    • Git 配置的 http.sslBackend 默认使用 Openssl 会默认使用自己证书的信任链,如果代理的证书没有被客户端 Git 自带的证书库信任就会出现这个问题,使用 schannel 可以避免非透明代理出现这个的问题。

  • 解决方法

    • 配置 Git 跳过 SSL 验证(不推荐)
    • 配置 Git 在使用代理时使用正确的证书链
    • 让 Git 使用 Windows 系统的证书存储来验证 SSL 连接

#问题背景

  • 在使用steamcommunity 302代理访问 Github 后,出现SSL certificate problem: unable to get local issuer certificate的错误,而且无法使用HTTPS上传项目到 Github 仓库了。

#原理解析

  • Git 使用 HTTPS 时验证 SSL 证书的基本流程

    • 建立 TCP 连接:

      • Git 客户端首先通过网络建立与远程服务器的 TCP 连接。

    • SSL/TLS 握手:

      • 客户端和服务器进行 SSL/TLS 握手,交换加密信息。

    • 服务器证书验证:

      • 服务器在握手过程中提供自己的 SSL 证书,Git 客户端需要验证这个证书是否是由受信任的证书颁发机构(CA)签发的,是否有效(未过期),以及是否与远程服务器的域名匹配。

    • 建立加密连接:

      • 验证通过后,双方使用共享的加密密钥进行加密通信,确保数据的安全性。

  • 调试 SSL 证书问题

    • 使用 GIT_CURL_VERBOSE 环境变量调试 Git 和服务器之间的 SSL/TLS 握手,能输出详细的 SSL 证书信息。
      • GIT_CURL_VERBOSE=1 git clone https://github.com/username/repo.git

    SSL证书调试信息

    • 通过上图使用代理前后 SSL 证书信息可以看出:

      1. 服务器返回被 CA 签名过的证书给客户端后
      2. 客户端验证该证书的然后强行中断了 TLS 连接
      3. 然后客户端通过 TCP 连接发送 TLS 协议中的警告通知,告知对方无法识别证书的颁发机构(CA)

    • 总结:也就是说问题在 TLS 证书传给客户端处出现验证失败。

  • 梳理该过程的证书链验证流程

    • 正常情况下证书链(或者使用透明代理 -> 代理正常转发证书链不会影响客户端和Github证书验证

      • GitHub 的服务器证书:这是 GitHub 服务器返回的原始证书,颁发给 github.com
      • GitHub 的中间证书:由受信任的 CA 机构签发,通常是 GitHub 使用的中间证书。
      • CA 的根证书:根证书由一个公认的受信任的 CA(如 DigiCert、Let’s Encrypt 等)签发。

    • 使用代理后的证书链(非透明代理 -> 常见的是使用 HTTPS 代理

      • 代理服务器的伪造证书:证书颁发给 github.com,但由代理服务器的中间证书签发。
      • 代理的中间证书:代理会附带由自己信任的中间证书,目的是为了使客户端能够验证伪造的证书。
      • 代理的根证书:如果代理是使用自签名的证书或由自己的私有 CA 颁发证书链,客户端必须信任这个根证书,否则会出现证书验证失败的错误。

    • 总结: 也就是说非透明代理修改了 Github 和客户端之间的 TLS 建立过程。

  • 解决思路

    • 配置 Git 跳过 SSL 验证(不推荐)

    • 配置 Git 在使用代理时使用正确的证书链

    • 让 Git 使用 Windows 系统的证书存储来验证 SSL 连接

      • Git 配置 http.sslBackend 使用 Schannel[1] (Windows SSL/TLS 栈)
        • 客户端和代理建立连接时,会使用 Windows 操作系统的证书存储来验证 Github 服务器证书,不需要代理生成任何伪造的证书。

        • 也就是说会直接使用 Windows 系统中已安装的证书(包括根证书和中间证书)来验证服务器证书。如果代理的证书是由一个已知的受信 CA 签发的,客户端(Windows)会信任它。


#解决方法

  • 配置 Git 跳过 SSL 验证(不推荐)

    • 暂时禁用 SSL 验证(不推荐长期使用)
      • git config --global http.sslVerify false

  • 配置 Git 在使用代理时使用正确的证书链

    • 这里由于代理相关信息不清楚,就暂时不考虑
      • 确保代理配置正确,再考虑添加代理证书链
        • git config --global http.sslCAinfo /path/to/proxy/certificate.pem

  • 让 Git 使用 Windows 系统的证书存储来验证 SSL 连接

    • http.sslBackend: 这个配置项控制 Git 使用哪种 SSL/TLS 库来进行 HTTPS 连接的证书验证。
      • Schannel:[1]

        • 会使用 Windows 操作系统的证书存储来验证 Github 服务器证书。
        • git config --global http.sslBackend schannel

      • Openssl:[2]

        • 是一个独立的 SSL/TLS 实现,它不像 Schannel 那样直接使用操作系统的证书存储,而是依赖于自己的信任根证书库。默认情况下,OpenSSL 会检查客户端信任的根证书存储(如 ca-certificates)。
        • git config --global http.sslBackend openssl

  • 相关补充

    • Git 测试 HTTPS 连接

      • git ls-remote https://github.com/username/repo.git

    • Git 测试 SSH 连接

    • Git 测试详细的 SSL 证书信息

      • GIT_CURL_VERBOSE=1 git clone https://github.com/username/repo.git

    • 设置 Git 使用 HTTP/HTTPS 代理:

      • git config --global http.proxy http://<代理地址>:<代理端口>
      • git config --global https.proxy http://<代理地址>:<代理端口>

    • 查看 OpenSSL 使用的证书存储位置(默认是Git安装目录下/mingw64/ssl)

      • openssl version -d


  1. 1.Schannel 使用的是 Windows 操作系统的证书存储,它默认包含了很多受信任的根证书,因此如果代理的证书是由一个已知的受信 CA 签发的,客户端(Windows)会信任它。
  2. 2.OpenSSL 默认使用自己证书的信任链,需要确保代理使用的证书链能够被 OpenSSL 信任。若使用的是自签名证书或者未被信任的 CA,需要显式地将根证书或中间证书导入 OpenSSL 的信任列表。