【Claude】SSL certificate verification 错误:证书校验失败与自定义 CA 配置

关键词: Claude Code、SSL certificate verification、TLS 证书、自定义 CA、企业代理、证书链、NODE_EXTRA_CA_CERTS、SSL Inspection、中间人攻击、证书过期、unable to verify the first certificate、SELF_SIGNED_CERT_IN_CHAIN


一、问题描述:当安全成为障碍

在现代企业网络环境中,SSL/TLS 证书验证失败是 Claude Code 和其他基于 Node.js 的 CLI 工具最常见的网络类错误之一。与普通的网络连接超时或 403 拒绝访问不同,SSL 证书错误通常具有更强的"技术感"和更令人困惑的报错信息。当你看到 SSL certificate verification failedSelf-signed certificate detected 时,你的第一反应可能是"Anthropic 的证书过期了?"或者"我的系统时间不对?"但实际上,在绝大多数情况下,这个问题的根源在于你的企业网络环境,而不是 Anthropic 的服务器

1.1 典型报错场景与信息

场景一:Claude Code 启动时连接 API 失败
Unable to connect to API: SSL certificate verification failed. Check your proxy or corporate SSL certificates.

或:

Unable to connect to API: Self-signed certificate detected
场景二:Python SDK 调用报错
import anthropic
client = anthropic.Anthropic(api_key="sk-ant-api03-...")
response = client.messages.create(...)

异常信息:

anthropic.APIConnectionError: Connection error.
Caused by: SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1129)
场景三:Node.js 底层错误
Error: unable to verify the first certificate
    at TLSSocket.onConnectSecure (node:_tls_wrap:1530:34)
    at TLSSocket.emit (node:events:525:35)
    at TLSSocket._finishInit (node:_tls_wrap:994:8)
    at TLSWrap.ssl.onhandshakedone (node:_tls_wrap:778:12)
场景四:curl 命令成功但 Claude Code 失败

这是最典型的"证书问题"标志:

# curl 可以正常访问
$ curl -I https://api.anthropic.com
HTTP/2 200

# 但 Claude Code 报错 SSL 证书错误
$ claude -p "test"
Unable to connect to API: SSL certificate verification failed

1.3 核心认知:curl 能访问 ≠ 工具能访问

这是理解 SSL 证书问题的关键:

  • curl 使用操作系统的证书存储(macOS Keychain、Windows 证书存储、Linux /etc/ssl/certs)
  • Claude Code(Node.js) 使用 Node.js 内置的 CA 证书存储,与操作系统证书存储是独立的
  • Python SDK 使用 certifi 包提供的 CA 证书包,或 Python 的 ssl 模块默认配置

因此,即使你的系统已经正确安装了企业 CA 证书,Claude Code 也不会自动信任它,因为 Node.js 不知道系统证书存储中有什么。


23

二、根因分析:企业 TLS 拦截与证书信任链

2.1 为什么企业网络会拦截 SSL 流量?

在现代企业环境中,TLS 流量检查(SSL Inspection / TLS Decryption) 是一种标准的安全实践。企业通过部署代理服务器(如 Zscaler、Palo Alto、Fortinet、Blue Coat、Squid 等),对所有出站 HTTPS 流量进行"中间人"(Man-in-the-Middle, MITM)式的检查:

正常 HTTPS 连接流程
[客户端] ←──TLS 加密──→ [服务器]
            直接加密通信,企业无法查看内容
企业代理 TLS 拦截流程
[客户端] ←──TLS A──→ [企业代理] ←──TLS B──→ [目标服务器]
                         ↑
                    代理解密检查内容,
                    使用企业 CA 重新签名

在这个流程中:

  1. 客户端向目标服务器发起 HTTPS 连接
  2. 企业代理拦截该连接,代替目标服务器与客户端建立 TLS 连接(TLS A)
  3. 代理使用自己的证书(由企业 CA 签发)向客户端出示服务器身份
  4. 同时,代理与真正的目标服务器建立另一个 TLS 连接(TLS B)
  5. 代理可以检查、记录、过滤两个方向的所有流量内容

2.2 为什么这会导致 Claude Code 报错?

当企业代理使用企业 CA 签发的证书与客户端建立 TLS 连接时,客户端必须信任该企业 CA,否则 TLS 握手会失败。具体在 Claude Code 的场景中:

信任链断裂的过程
  1. Claude Code(Node.js 运行时)尝试连接 https://api.anthropic.com
  2. 企业代理拦截该请求,返回一个由企业 CA 签名的证书(证书中的 CN/SAN 可能是 api.anthropic.com,但签发者是 AcmeCorp Internal CA
  3. Node.js 收到该证书后,尝试验证证书链:
    • 检查证书是否由可信 CA 签发 → 发现签发者是 AcmeCorp Internal CA
    • 在 Node.js 内置的 CA 列表中查找 AcmeCorp Internal CA找不到
    • 证书验证失败 → 抛出 SSL certificate verification failed
对比:curl 为什么能成功?

因为企业的 IT 部门已经通过组策略(Windows)、MDM(macOS)或系统配置工具,将企业 CA 证书安装到了操作系统的信任存储中。curl 使用系统信任存储,所以它能验证由企业 CA 签发的证书。但 Node.js 不使用系统信任存储,它只信任自己内置的 CA 列表。

2.3 Node.js 的证书体系:独立于操作系统

这是 Node.js 的一个设计特性(也可以说是限制):

操作系统信任存储(curl、浏览器、系统应用使用)
    ├── macOS: Keychain
    ├── Windows: 证书管理器
    └── Linux: /etc/ssl/certs/

        ↑ 互相独立,不自动同步

Node.js 内置 CA 存储(Node.js 应用使用)
    ├── 编译时内置的 Mozilla CA 列表
    ├── 可以通过 NODE_EXTRA_CA_CERTS 扩展
    └── 可以通过 --use-openssl-ca 使用系统 CA(但默认不启用)

关键结论:要让 Node.js 应用(包括 Claude Code)信任企业 CA,必须显式配置 NODE_EXTRA_CA_CERTS 环境变量,指向企业 CA 证书文件。

2.4 其他可能的证书错误原因

除了企业代理 TLS 拦截,还有以下原因可能导致证书错误:

原因 典型错误信息 常见场景
证书过期 CERT_HAS_EXPIRED 使用了自签名证书且未更新;企业 CA 证书未及时续期
证书链不完整 UNABLE_TO_VERIFY_LEAF_SIGNATURE 服务器只发送了叶子证书,没有包含中间 CA 证书
自签名证书 SELF_SIGNED_CERT_IN_CHAIN 开发环境使用自签名证书;企业代理配置不当
主机名不匹配 ERR_TLS_CERT_ALTNAME_INVALID 证书中的域名与实际访问的域名不一致
缺少中间证书 unable to verify the first certificate 代理配置错误,未发送完整的证书链
系统时间错误 certificate is not yet valid 客户端系统时间设置错误(早于证书生效时间)

三、实际操练:从诊断到修复的完整流程

本节将手把手教你如何诊断 SSL 证书问题,并在不同操作系统和企业网络环境中配置自定义 CA 证书。

3.1 第一步:分层诊断,确认问题层级

遇到 SSL 证书错误时,按照以下顺序排查,可以快速定位问题所在层级:

3.1.1 检查系统时间和网络连接
# 检查系统时间(确保与当前时间一致)
date
# 如果系统时间偏差超过几分钟,证书验证可能失败

# 检查基本网络连通性
ping -c 3 api.anthropic.com
# 或
curl -I https://api.anthropic.com

如果系统时间严重偏差,先修正系统时间:

# macOS
sudo sntp -sS time.apple.com

# Linux
sudo timedatectl set-ntp true
sudo systemctl restart systemd-timesyncd
3.1.2 测试不同工具的访问能力
# 测试 1:curl(使用系统 CA 存储)
curl -v -I https://api.anthropic.com 2>&1 | grep -E "SSL|TLS|HTTP|error"
# 如果 curl 成功 → 说明网络可达,服务器证书正常

# 测试 2:curl 使用系统 CA 显式(macOS)
curl -v --cacert /etc/ssl/cert.pem -I https://api.anthropic.com 2>&1 | grep -E "SSL|TLS|HTTP"

# 测试 3:openssl 检查证书链
openssl s_client -connect api.anthropic.com:443 -showcerts </dev/null 2>/dev/null | grep -E "Certificate chain|Subject:|Issuer:"

关键判断

  • 如果 curl 成功但 claude 失败 → 确定是 Node.js 证书存储问题
  • 如果 curl 也失败 → 可能是系统级证书问题或企业代理配置问题
3.1.3 检查是否经过企业代理
# 检查代理环境变量
echo $HTTPS_PROXY
echo $HTTP_PROXY
echo $http_proxy
echo $https_proxy

# 检查系统代理设置(macOS)
networksetup -getwebproxy "Wi-Fi"
networksetup -getsecurewebproxy "Wi-Fi"

# 检查是否有 PAC 文件配置
networksetup -getautoproxyurl "Wi-Fi"

如果存在代理,使用代理进行 curl 测试:

curl -v -x "$HTTPS_PROXY" -I https://api.anthropic.com 2>&1 | grep -E "SSL|HTTP|error"
3.1.4 检查证书链详情
# 使用 openssl 查看完整的证书链
openssl s_client -connect api.anthropic.com:443 -showcerts -servername api.anthropic.com </dev/null

正常输出示例(无代理):

Certificate chain
 0 s:CN = api.anthropic.com
   i:C = US, O = Let's Encrypt, CN = R3
 1 s:C = US, O = Let's Encrypt, CN = R3
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1

异常输出示例(有企业代理拦截):

Certificate chain
 0 s:CN = api.anthropic.com
   i:C = US, O = AcmeCorp, OU = IT, CN = AcmeCorp Internal CA
 1 s:C = US, O = AcmeCorp, OU = IT, CN = AcmeCorp Internal CA
   i:C = US, O = AcmeCorp, OU = IT, CN = AcmeCorp Root CA

如果 Issuer(i:)显示的是企业名称而不是公共 CA(如 Let's Encrypt、DigiCert),说明流量正在被企业代理拦截。

3.2 第二步:获取企业 CA 证书

确认需要企业 CA 证书后,下一步是获取该证书文件。以下是不同场景下的获取方法:

3.2.1 从 macOS Keychain 导出(推荐)

如果你的 Mac 已经通过企业配置或手动安装信任了企业 CA,可以从 Keychain 中导出:

# 查找企业 CA 证书(替换 "AcmeCorp" 为你的企业名称)
security find-certificate -a -c "AcmeCorp" -p /Library/Keychains/System.keychain

# 导出所有用户级证书到文件
security find-certificate -a -p /Library/Keychains/System.keychain > ~/corp-ca.pem

# 或导出系统级证书
security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain >> ~/corp-ca.pem

# 合并用户级和系统级证书
security find-certificate -a -p ~/Library/Keychains/login.keychain-db >> ~/corp-ca.pem
security find-certificate -a -p /Library/Keychains/System.keychain >> ~/corp-ca.pem

注意 macOS 的特殊情况

macOS 上的证书可以通过 security add-trusted-cert -p ssl 添加,这种方式添加的证书被 Safari 和 Chrome 信任,但不被 Node.js 的 keychain 读取器信任。如果你发现即使证书已安装在 Keychain 中,Claude Code 仍然报错,需要:

# 重新添加证书,使用完整的 root 信任(不限制 -p ssl)
sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain /path/to/corp-ca.pem

# 或者显式导出并设置 NODE_EXTRA_CA_CERTS
3.2.2 从浏览器导出
  1. 打开 Chrome 或 Safari,访问 https://api.anthropic.com
  2. 点击地址栏的锁形图标 → 证书信息
  3. 在证书链中找到企业 CA 证书(不是叶子证书,也不是根证书,通常是中间层)
  4. 导出为 .pem.crt 格式
  5. 如果导出的是二进制格式(DER),需要转换为 PEM:
openssl x509 -in corp-ca.crt -inform DER -out corp-ca.pem -outform PEM
3.2.3 从 openssl 连接中提取
# 连接到目标,提取证书链
openssl s_client -connect api.anthropic.com:443 -showcerts </dev/null 2>/dev/null > /tmp/certs.pem

# 查看提取的证书
cat /tmp/certs.pem | openssl crl2pkcs7 -nocrl -certfile /dev/stdin | openssl pkcs7 -print_certs -noout

# 从链中提取企业 CA 证书(通常是第二个证书,即 chain 中的第 1 层)
# 手动编辑 /tmp/certs.pem,只保留企业 CA 证书部分
3.2.4 从 IT 部门获取

最可靠的方法是直接向企业 IT 部门索取:

  • "请提供公司的根 CA 证书和中间 CA 证书(PEM 格式)"
  • 通常 IT 部门会有标准的 .pem.crt 文件用于分发
3.2.5 验证证书文件格式

获取证书后,验证其格式正确:

# 检查 PEM 格式(应该以 -----BEGIN CERTIFICATE----- 开头)
head -n 5 ~/corp-ca.pem
# 预期输出:
# -----BEGIN CERTIFICATE-----
# MIIDXTCCAkWgAwIBAgIJAJC1HiIAZAiUMA0GCSqGSIb3...
# ...

# 查看证书信息
openssl x509 -in ~/corp-ca.pem -noout -text | grep -E "Subject:|Issuer:|Not Before|Not After"

# 确认证书是 CA 证书
openssl x509 -in ~/corp-ca.pem -noout -text | grep -A1 "Basic Constraints"
# 应该包含:CA:TRUE

3.3 第三步:配置 Claude Code 使用自定义 CA 证书

获取并验证企业 CA 证书后,需要配置 Claude Code(Node.js)信任该证书。

3.3.1 设置 NODE_EXTRA_CA_CERTS(推荐方法)

这是官方推荐的方法,也是最为可靠的方法:

临时设置(当前终端会话)

export NODE_EXTRA_CA_CERTS="$HOME/corp-ca.pem"
claude

永久设置(添加到 shell 配置)

macOS (zsh)

# 编辑 ~/.zshrc
vim ~/.zshrc

# 添加以下行(根据实际路径调整)
export NODE_EXTRA_CA_CERTS="$HOME/corp-ca.pem"

# 保存并重新加载
source ~/.zshrc

Linux (bash)

vim ~/.bashrc
export NODE_EXTRA_CA_CERTS="/home/username/corp-ca.pem"
source ~/.bashrc

Windows (PowerShell 配置文件)

# 在 $PROFILE 中添加
$env:NODE_EXTRA_CA_CERTS = "$HOME\corp-ca.pem"

Windows (系统环境变量)

  1. 设置 → 系统 → 关于 → 高级系统设置 → 环境变量
  2. 点击"新建",变量名:NODE_EXTRA_CA_CERTS
  3. 变量值:C:\Users\Username\corp-ca.pem
  4. 重启所有终端和 IDE
3.3.2 验证配置是否生效
# 1. 检查环境变量已设置
echo $NODE_EXTRA_CA_CERTS
# 预期输出:/Users/username/corp-ca.pem

# 2. 启动 Claude Code 并测试
claude -p "Hello, please respond with 'OK' if you can read this."
# 预期输出:OK

# 3. 在 Claude Code 中验证连接
# 输入 /status 确认没有 SSL 错误

# 4. 使用 Node.js 直接测试
node -e "
const https = require('https');
https.get('https://api.anthropic.com', { headers: { 'anthropic-version': '2023-06-01' } }, (res) => {
  console.log('Status:', res.statusCode);
  console.log('OK - SSL connection successful');
}).on('error', (e) => {
  console.error('SSL Error:', e.message);
});
"
# 预期输出:Status: 401 或 Status: 405(认证或方法错误,但 SSL 成功)
# 如果看到 SSL Error: ... 则配置可能有问题
3.3.3 处理多个 CA 证书的情况

如果企业使用了多个 CA(根 CA + 中间 CA),需要将所有相关证书合并到一个 PEM 文件中:

# 合并多个证书文件
cat corp-root-ca.pem corp-intermediate-ca.pem > corp-ca-bundle.pem

# 验证合并后的文件包含多个证书
openssl storeutl -certs corp-ca-bundle.pem | grep -c "Certificate"
# 预期输出证书数量,如 2

# 使用合并后的文件
export NODE_EXTRA_CA_CERTS="$HOME/corp-ca-bundle.pem"

3.4 第四步:配置 Python SDK 的 CA 证书(如果需要)

如果你同时使用 Python SDK,也需要为其配置 CA 证书:

方法 1:使用 certifi 包
# 安装 certifi
pip install certifi

# 查看 certifi 的默认证书路径
python3 -c "import certifi; print(certifi.where())"
# 输出类似:/usr/local/lib/python3.11/site-packages/certifi/cacert.pem

# 将企业 CA 证书追加到 certifi 的证书包中
cat ~/corp-ca.pem >> $(python3 -c "import certifi; print(certifi.where())")

注意:这种方法在升级 certifi 包后会被覆盖,需要重新追加。

方法 2:使用环境变量 REQUESTS_CA_BUNDLE
export REQUESTS_CA_BUNDLE="$HOME/corp-ca.pem"
# 这会同时影响 requests 库和 httpx 库的 SSL 验证
方法 3:使用 SSL_CERT_FILE 和 SSL_CERT_DIR
export SSL_CERT_FILE="$HOME/corp-ca.pem"
# 或
export SSL_CERT_DIR="/path/to/certs/directory"

3.5 第五步:处理 Claude Code 的 MCP 服务器证书

MCP 服务器可能是由 Claude Code 启动的 Node.js 进程,需要确保它们也能继承正确的证书配置:

# 在 shell 配置中同时设置多个环境变量
export NODE_EXTRA_CA_CERTS="$HOME/corp-ca.pem"
export REQUESTS_CA_BUNDLE="$HOME/corp-ca.pem"
export SSL_CERT_FILE="$HOME/corp-ca.pem"

# 启动 Claude Code,MCP 子进程将继承这些环境变量
claude

或者在 Claude Code 的配置中设置:

# 在 ~/.zshrc 或 ~/.bashrc 中添加
export NODE_EXTRA_CA_CERTS="$HOME/corp-ca.pem"
export REQUESTS_CA_BUNDLE="$HOME/corp-ca.pem"

3.6 第六步:其他操作系统和特殊情况

3.6.1 Linux 系统配置

在 Linux 上,除了 NODE_EXTRA_CA_CERTS,还可以将企业 CA 添加到系统证书目录:

# 将证书复制到系统证书目录
sudo cp corp-ca.pem /usr/local/share/ca-certificates/corp-ca.crt

# 更新系统证书存储
sudo update-ca-certificates

# 注意:这不会自动影响 Node.js,仍然需要设置 NODE_EXTRA_CA_CERTS
3.6.2 Windows 系统配置

在 Windows 上:

  1. 安装证书到系统存储

    • 双击 .crt.pem 文件
    • 选择"安装证书" → "本地计算机" → "将所有的证书放入下列存储" → "受信任的根证书颁发机构"
  2. 配置 Node.js 环境变量

    [System.Environment]::SetEnvironmentVariable(
        "NODE_EXTRA_CA_CERTS",
        "C:\Users\Username\corp-ca.pem",
        [System.EnvironmentVariableTarget]::User
    )
    
  3. 对于 WSL 环境

    • WSL 有自己的证书存储,需要单独配置
    • 将证书复制到 WSL 中:cp /mnt/c/Users/Username/corp-ca.pem ~/corp-ca.pem
    • 在 WSL 的 ~/.bashrc 中设置 NODE_EXTRA_CA_CERTS
3.6.3 容器/沙箱环境

如果在 Docker 容器或 Claude 云会话中使用 Claude Code,需要将 CA 证书挂载到容器中:

# Dockerfile 示例
COPY corp-ca.pem /usr/local/share/ca-certificates/
RUN update-ca-certificates
ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corp-ca.pem

对于 Claude 云会话,由于云会话的运行环境由 Anthropic 管理,通常无法直接修改系统证书存储。但如果你的代理配置了自定义域名,请参考 host_not_allowed 错误的配置方法,同时确保本地环境变量正确设置。


四、绝不使用的"危险快捷方式"

在排查 SSL 证书问题时,你可能会在网上看到一些"快速解决方案"。以下方法虽然能暂时消除错误,但会带来严重的安全隐患,绝不能在生产环境或任何重要场景中使用

4.1 NODE_TLS_REJECT_UNAUTHORIZED=0

# 这是最安全问题的"解决方案"
export NODE_TLS_REJECT_UNAUTHORIZED=0
claude

为什么危险

  • 这会完全禁用 Node.js 的 所有 TLS 证书验证
  • Claude Code 将不再验证任何 HTTPS 服务器的证书真实性
  • 攻击者可以轻松伪造 api.anthropic.com 的证书进行中间人攻击
  • 你的 API Key 和对话内容可能被窃取

唯一可接受的临时使用场景:在完全隔离的本地开发环境中,仅用于确认问题确实是证书相关。使用完毕后必须立即取消设置。

# 临时测试(确认后马上取消)
export NODE_TLS_REJECT_UNAUTHORIZED=0
claude -p "test"  # 如果成功,确认是证书问题
unset NODE_TLS_REJECT_UNAUTHORIZED  # 立即取消!

# 然后使用正确的方式配置 NODE_EXTRA_CA_CERTS

4.2 使用未经验证的自签名证书

有些开发者在内部环境使用自签名证书,但没有正确配置信任链。如果 Claude Code 需要连接这类服务,正确的做法是将自签名证书添加到信任列表,而不是全局禁用验证。

# 正确做法:将自签名证书添加到信任列表
export NODE_EXTRA_CA_CERTS="/path/to/self-signed-cert.pem"

# 错误做法:禁用所有验证
export NODE_TLS_REJECT_UNAUTHORIZED=0

五、高级诊断技巧与工具

5.1 使用 Claude Code 的调试模式

# 启用调试日志
claude --debug -p "test" 2>&1 | tee claude-debug.log

# 从日志中查找 SSL 相关信息
grep -E "SSL|TLS|CERT|verify|reject" claude-debug.log

5.2 创建网络诊断脚本

#!/bin/bash
# diagnose-ssl.sh - SSL 证书问题诊断脚本

echo "=== SSL 证书问题诊断 ==="

# 1. 检查系统时间
echo "[1/6] 系统时间检查..."
date

# 2. 检查代理环境变量
echo "[2/6] 代理环境变量..."
echo "HTTPS_PROXY=$HTTPS_PROXY"
echo "HTTP_PROXY=$HTTP_PROXY"

# 3. 检查 CA 证书环境变量
echo "[3/6] CA 证书环境变量..."
echo "NODE_EXTRA_CA_CERTS=$NODE_EXTRA_CA_CERTS"
echo "SSL_CERT_FILE=$SSL_CERT_FILE"
echo "REQUESTS_CA_BUNDLE=$REQUESTS_CA_BUNDLE"

# 4. curl 测试
echo "[4/6] curl 测试..."
if curl -s -o /dev/null -w "%{http_code}" -I https://api.anthropic.com | grep -q "200\|401\|405"; then
    echo "✅ curl 成功(HTTP 200/401/405)"
else
    echo "❌ curl 失败"
fi

# 5. openssl 证书链检查
echo "[5/6] openssl 证书链检查..."
openssl s_client -connect api.anthropic.com:443 -servername api.anthropic.com </dev/null 2>/dev/null | grep -E "Certificate chain|Verify return code"

# 6. Node.js 测试
echo "[6/6] Node.js SSL 测试..."
node -e "
const tls = require('tls');
const socket = tls.connect(443, 'api.anthropic.com', { servername: 'api.anthropic.com' }, () => {
  console.log('✅ Node.js TLS 连接成功');
  console.log('证书签发者:', socket.getPeerCertificate().issuer.O);
  socket.end();
});
socket.on('error', (err) => {
  console.log('❌ Node.js TLS 错误:', err.message);
});
"

echo "=== 诊断完成 ==="

5.3 处理证书链不完整的情况

如果 openssl 输出显示 Verify return code: 21 (unable to verify the first certificate),说明证书链不完整。需要:

  1. 获取中间 CA 证书
  2. 将其与根 CA 证书合并
  3. 重新配置 NODE_EXTRA_CA_CERTS
# 从 openssl 输出中提取中间证书
openssl s_client -connect api.anthropic.com:443 -showcerts </dev/null 2>/dev/null > /tmp/fullchain.pem

# 将 fullchain 中的中间证书分离出来
# 手动编辑 /tmp/fullchain.pem,将中间证书保存为 intermediate.pem

# 合并根证书和中间证书
cat root-ca.pem intermediate.pem > ca-bundle.pem
export NODE_EXTRA_CA_CERTS="$PWD/ca-bundle.pem"

六、验证与回归测试

6.1 验证清单

验证项 验证方法 预期结果
环境变量已设置 echo $NODE_EXTRA_CA_CERTS 输出证书文件路径
证书文件存在 ls -la $NODE_EXTRA_CA_CERTS 文件存在且可读
证书格式正确 openssl x509 -in $NODE_EXTRA_CA_CERTS -noout -text 显示证书信息,无错误
Node.js 连接成功 node -e "require('https').get('https://api.anthropic.com', (r) => console.log(r.statusCode))" 输出 401 或 405(非 SSL 错误)
Claude Code 连接成功 claude -p "test" 正常输出
Python SDK 连接成功 运行测试脚本 正常响应
MCP 服务器连接正常 在 Claude Code 中使用 MCP 工具 工具正常响应

6.2 回归测试脚本

#!/bin/bash
# ssl-regression-test.sh

echo "=== SSL 证书配置回归测试 ==="

PASS=0
FAIL=0

# 测试 1:环境变量
echo -n "[1/4] NODE_EXTRA_CA_CERTS 环境变量 ... "
if [ -n "$NODE_EXTRA_CA_CERTS" ] && [ -f "$NODE_EXTRA_CA_CERTS" ]; then
    echo "✅ PASS"
    PASS=$((PASS + 1))
else
    echo "❌ FAIL"
    FAIL=$((FAIL + 1))
fi

# 测试 2:证书文件格式
echo -n "[2/4] 证书文件格式 ... "
if openssl x509 -in "$NODE_EXTRA_CA_CERTS" -noout 2>/dev/null; then
    echo "✅ PASS"
    PASS=$((PASS + 1))
else
    echo "❌ FAIL"
    FAIL=$((FAIL + 1))
fi

# 测试 3:Node.js HTTPS 连接
echo -n "[3/4] Node.js HTTPS 连接 ... "
if node -e "
const https = require('https');
https.get('https://api.anthropic.com', {headers:{'anthropic-version':'2023-06-01'}}, (r) => {
  if (r.statusCode) { console.log('OK'); process.exit(0); }
}).on('error', () => { console.log('FAIL'); process.exit(1); });
setTimeout(() => { console.log('TIMEOUT'); process.exit(1); }, 10000);
" 2>/dev/null | grep -q "OK"; then
    echo "✅ PASS"
    PASS=$((PASS + 1))
else
    echo "❌ FAIL"
    FAIL=$((FAIL + 1))
fi

# 测试 4:Claude Code 非交互模式
echo -n "[4/4] Claude Code 连接 ... "
if claude -p "Respond with OK" 2>/dev/null | grep -qi "OK"; then
    echo "✅ PASS"
    PASS=$((PASS + 1))
else
    echo "❌ FAIL"
    FAIL=$((FAIL + 1))
fi

echo ""
echo "结果:$PASS 通过,$FAIL 失败"
[ $FAIL -eq 0 ] && exit 0 || exit 1

七、总结与最佳实践

7.1 核心要点回顾

  1. Claude Code 的 SSL 错误通常源于企业代理:不是 Anthropic 服务器的问题
  2. Node.js 使用独立的 CA 存储:操作系统信任了企业 CA 不等于 Node.js 也信任
  3. NODE_EXTRA_CA_CERTS 是标准解决方案:指向包含企业 CA 的 PEM 文件
  4. 绝不使用 NODE_TLS_REJECT_UNAUTHORIZED=0:这等于关闭所有 HTTPS 安全验证
  5. 证书链完整性很重要:确保包含中间 CA 证书,不仅仅是根证书

7.2 企业环境最佳实践

实践 说明
标准化证书分发 IT 部门应提供标准化的 PEM 格式 CA 证书包,并建立内部文档
自动化配置 使用 Ansible、Chef、Puppet 等工具在新员工机器上自动配置 NODE_EXTRA_CA_CERTS
入职文档 在开发者入职文档中明确说明 Claude Code 的 CA 证书配置要求
定期更新 当企业 CA 证书续期或变更时,及时更新所有开发者的证书文件
版本控制 将 corp-ca.pem 存放在内部 Git 仓库中,便于团队同步更新
CI/CD 集成 在 CI/CD 流水线中自动注入 CA 证书环境变量

7.3 常见错误速查表

错误信息 原因 解决方案
SSL certificate verification failed 企业代理证书不被 Node.js 信任 设置 NODE_EXTRA_CA_CERTS
Self-signed certificate detected 代理使用了自签名证书 将自签名证书添加到 NODE_EXTRA_CA_CERTS
unable to verify the first certificate 证书链不完整,缺少中间 CA 补充中间 CA 证书到证书包
CERT_HAS_EXPIRED 企业 CA 证书已过期 联系 IT 更新证书,或临时使用有效证书
ERR_TLS_CERT_ALTNAME_INVALID 证书中的域名与实际访问的域名不匹配 检查是否有 DNS 劫持或配置错误
curl 成功但 claude 失败 Node.js 和 curl 使用不同的证书存储 配置 NODE_EXTRA_CA_CERTS 而非系统证书
设置了 CA 证书仍然报错 证书格式错误 / 路径错误 / 未包含中间证书 验证 PEM 格式、检查路径、确保证书链完整

八、参考资料

  1. Anthropic 官方错误参考 - SSL 部分https://code.claude.com/docs/zh-CN/errors
  2. Claude Code 企业网络配置文档https://docs.anthropic.com/zh-TW/docs/claude-code/network-config
  3. Node.js TLS 文档https://nodejs.org/api/tls.html
  4. Node.js NODE_EXTRA_CA_CERTS 说明https://nodejs.org/api/cli.html#node_extra_ca_certsfile
  5. 企业防火墙中的 AI 编程 CLI 配置指南https://inventivehq.com/blog/ai-coding-cli-corporate-firewall-proxy-fixes
  6. ClaudHQ SSL 证书错误修复指南https://claudhq.com/claude-code-ssl-certificate-verification-failed-error
  7. SSL Certificate Chain 不完整修复https://claudecodeguides.com/claude-code-ssl-certificate-chain-incomplete-fix-2026

版权声明:本文为原创技术文章,基于实际操练和官方文档撰写。欢迎转载,但请注明出处。

版本记录:v1.0 | 2026-06-21 | 初稿完成,覆盖 SSL 证书验证错误的完整诊断与自定义 CA 配置流程

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐