Windows自动更新PHP中curl常用的证书集cacert.pem

生活日记 工作心得
8
发表时间: 编辑:顾永胜来源:0513.city标签:原创

PHP 本身不自带cacert.pem,这个文件来自 curl 项目,是从 Mozilla NSS 根证书库 (certdata.txt) 提取导出的根证书束 bundle。PHP 的 curl.cainfo / openssl.cafile 配置项指向它,用于 HTTPS 校验服务器证书链,解决 cURL error 60 问题。

定期更新,Mozilla 撤销、新增根证书后 curl 会同步发布新版本,PHP 环境建议定期替换,避免老根证书过期导致 HTTPS 报错。

下面是windows PowerShell代码,用于定时任务,更新cacert.pem文件,可以设置每月检查一次。

param(
    [string]$CertUrl = "https://curl.se/ca/cacert.pem",
    [string]$TargetPath = "D:\php832\extras\ssl\cacert.pem",
    [string]$LogPath = "D:\php832\logs\cacert-update.log",
    [int]$RenewThresholdDays = 90   # 文件超过90天未更新就重新下载
)
$LogDir = Split-Path $LogPath -Parent
if (!(Test-Path $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null }
function Write-Log {
    param([string]$Message, [string]$Level = "INFO")
    $Line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [$Level$Message"
    Write-Host $Line
    Add-Content -Path $LogPath -Value $Line -Encoding UTF8
}
try {
    Write-Log "开始检查 cacert.pem..."
    # 1. 检查文件是否存在及修改时间
    if (Test-Path $TargetPath) {
        $LastWrite = (Get-Item $TargetPath).LastWriteTime
        $DaysSinceUpdate = ((Get-Date) - $LastWrite).Days
        if ($DaysSinceUpdate -lt $RenewThresholdDays) {
            Write-Log "证书包 $DaysSinceUpdate 天前更新,小于阈值 $RenewThresholdDays 天,无需更新" "INFO"
            exit 0
        } else {
            Write-Log "证书包已 $DaysSinceUpdate 天未更新,开始下载..." "INFO"
        }
    } else {
        Write-Log "本地证书不存在,需要下载" "WARN"
    }
    # 2. 下载
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    $TempFile = "$env:TEMP\cacert.pem.tmp"
    Invoke-WebRequest -Uri $CertUrl -OutFile $TempFile -UseBasicParsing -TimeoutSec 30
    $Content = Get-Content $TempFile -Raw
    if ($Content -notmatch "BEGIN CERTIFICATE") { throw "下载内容不是有效 PEM" }
    # 3. 验证下载成功(简单检查大小)
    $FileSize = (Get-Item $TempFile).Length
    if ($FileSize -lt 100000) { throw "下载文件过小($FileSize 字节),可能不完整" }
    Write-Log "下载成功,文件大小: $FileSize 字节"
    # 4. 备份旧证书(覆盖式,只保留一个)
    if (Test-Path $TargetPath) {
        Copy-Item $TargetPath "$TargetPath.backup" -Force
        Write-Log "旧证书已备份到: $TargetPath.backup"
    }
    # 5. 替换
    Copy-Item $TempFile $TargetPath -Force
    Remove-Item $TempFile -Force
    Write-Log "证书已替换,立即生效 ✅"
catch {
    if (Test-Path "$env:TEMP\cacert.pem.tmp") { Remove-Item "$env:TEMP\cacert.pem.tmp" -Force }
    Write-Log "更新失败 ❌: $_" "ERROR"
    exit 1
}