解决irm:基础连接已经关闭:发送时发送错误

2021-02-15 Windows

在使用 PowerShell 执行 Invoke-WebRequest 或 Invoke-RestMethod(以及它们的别名 iwr 和 irm)下载或请求 HTTPS 资源时,经常遇到以下错误:

irm : 基础连接已经关闭:发送时发送错误。
(The underlying connection was closed: An error occurred while sending.)

这个错误通常由 TLS/SSL 协议版本不匹配 引起,尤其是在较旧的 Windows 版本或未安装更新的 PowerShell 环境中。

本文将深入分析问题原因,并提供三种有效的解决方案。

错误原因分析

什么是 TLS?

TLS(Transport Layer Security,传输层安全协议)是 HTTPS 连接的基础安全协议。它的主要版本包括:

协议版本 发布年份 安全状态 当前使用
SSL 2.0 1995 ❌ 极不安全 已弃用
SSL 3.0 1996 ❌ 不安全(POODLE漏洞) 已禁用
TLS 1.0 1999 ⚠️ 不够安全 大部分现代网站已禁用
TLS 1.1 2006 ⚠️ 不够安全 逐渐被禁用
TLS 1.2 2008 ✅ 安全 当前标准
TLS 1.3 2018 ✅ 最安全 最新标准

问题根源

当你使用 PowerShell 的 irm 命令连接某个 HTTPS 服务器时:

  1. PowerShell 会使用系统默认的 TLS 协议版本发起连接
  2. 如果目标服务器要求使用 TLS 1.2 或更高版本
  3. 而你的系统默认只启用了 TLS 1.0/1.1
  4. 或者仅启用了 SSL 3.0
  5. 连接将被服务器拒绝,抛出"基础连接已经关闭"错误

常见触发场景:

  • 使用 irm 下载 GitHub 上的脚本(如 irm get.scoop.sh)
  • 调用 REST API 接口
  • 通过 PowerShell 下载文件
  • 在 Windows 7 / Server 2012 等旧系统上运行脚本
  • 未安装 .NET Framework 更新的系统

错误信息原文:

Invoke-WebRequest : 基础连接已经关闭:发送时发生错误。
The underlying connection was closed: An error occurred while sending.

Invoke-RestMethod : 基础连接已经关闭:接收时发生错误。
The underlying connection was closed: An error occurred while receiving.

方法一:在 PowerShell 中指定 TLS 版本(推荐)

在脚本文本执行 irm 之前,添加以下代码强制使用 TLS 1.2:

PowerShell 5.0/5.1

在脚本开头添加:

# 强制使用 TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

然后正常执行 irm:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
irm https://example.com/script.ps1

同时启用多个 TLS 版本

# 同时启用 TLS 1.0、1.1、1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls, 
    [Net.SecurityProtocolType]::Tls11, 
    [Net.SecurityProtocolType]::Tls12

使用位运算(更简洁)

# 启用 TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12

PowerShell 7.x

PowerShell 7 默认支持 TLS 1.2 和 1.3,但仍然可以手动指定:

[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13

添加到 $PROFILE(永久生效)

如果你希望每次打开 PowerShell 时都自动启用 TLS 1.2,可以将其添加到 PowerShell 配置文件中:

# 查看 $PROFILE 路径
$PROFILE

# 如果不存在,创建配置文件
if (!(Test-Path $PROFILE)) {
    New-Item -Path $PROFILE -ItemType File -Force
}

# 用记事本打开编辑
notepad $PROFILE

在文件中添加以下内容:

# 启用 TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12

保存后,重启 PowerShell 即可永久生效。

方法二:使用 curl 替代 irm

如果 PowerShell 的 TLS 设置无法解决,可以改用 Windows 自带的 curl.exe(注意不是 PowerShell 中的 curl 别名)。

在 PowerShell 中使用 curl.exe

curl.exe -L -o output.ps1 https://example.com/script.ps1

curl 常用参数说明

参数 说明 示例
-L 跟随重定向 -L
-o 输出到文件 -o file.txt
-O 使用远程文件名保存 -O
-k 跳过 SSL 证书验证 -k
-s 静默模式(不显示进度) -s
--tlsv1.2 强制使用 TLS 1.2 --tlsv1.2
-H 添加请求头 -H "Authorization: Bearer ..."

使用 curl 配合 TLS 1.2 下载脚本

curl.exe --tlsv1.2 -L -O https://raw.githubusercontent.com/example/script.ps1

使用 curl 替代 irm 执行远程脚本

传统 irm + iex(Invoke-Expression)模式:

# 旧方式(可能报错)
iex (irm https://example.com/install.ps1)

# 新方式(使用 curl)
iex (curl.exe --tlsv1.2 -sL https://example.com/install.ps1)

使用 wget 替代(如果有)

一些 Windows 系统也内置了 wget:

wget --secure-protocol=TLSv1_2 -O output.ps1 https://example.com/script.ps1

方法三:通过注册表全局启用 TLS 1.2

如果想在系统层面永久启用 TLS 1.2,可以修改注册表。

适用于 PowerShell 和 .NET Framework

以下注册表设置会强制所有 .NET 和 PowerShell 应用程序使用 TLS 1.2:

  1. 按 Win + R,输入 regedit,回车打开注册表编辑器

  2. 导航到以下路径:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319
  1. 在右侧空白处右键,新建 "DWORD (32位)值"(即使系统是 64 位也创建 32 位 DWORD)

  2. 创建以下两个键值:

键名 数值数据 说明
SystemDefaultTlsVersions 1 使用系统默认 TLS 版本
SchUseStrongCrypto 1 使用强加密算法,禁用 SSL 3.0 和 TLS 1.0
  1. 同样的设置应用到 32 位应用程序路径:
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319
  1. 同样创建:

    • SystemDefaultTlsVersions = 1
    • SchUseStrongCrypto = 1
  2. 对于 .NET Framework 2.0/3.5,重复上述步骤在以下路径:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v2.0.50727
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v2.0.50727
  1. 关闭注册表编辑器,重启电脑

使用注册表脚本一键导入

将以下内容保存为 enable-tls12.reg 文件,双击导入:

Windows Registry Editor Version 5.00

; .NET 4.x 64位
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

; .NET 4.x 32位
[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

; .NET 2.0/3.5 64位
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v2.0.50727]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

; .NET 2.0/3.5 32位
[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v2.0.50727]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

更新 .NET Framework 版本

如果上述注册表修改无效,可能需要更新 .NET Framework:

PowerShell 版本与 TLS 支持对照表

PowerShell 版本 默认 TLS 版本 手动设置 TLS 1.2
PowerShell 2.0 SSL 3.0 / TLS 1.0 ❌ 不支持
PowerShell 3.0 SSL 3.0 / TLS 1.0 ✅ 可行
PowerShell 4.0 SSL 3.0 / TLS 1.0 ✅ 可行
PowerShell 5.0 TLS 1.0 / 1.1 ✅ 可行
PowerShell 5.1 TLS 1.0 / 1.1 / 1.2 ✅ 原生支持
PowerShell 7.x TLS 1.2 / 1.3 ✅ 原生支持

如果你的 PowerShell 版本低于 5.1,建议更新到最新版本:

# 查看当前版本
$PSVersionTable.PSVersion

下载地址:https://aka.ms/powershell-release

常见错误状态与解决方案

错误信息 原因 解决方案
"基础连接已经关闭:发送时发送错误" TLS 版本不匹配 指定 TLS 1.2
"基础连接已经关闭:接收时发生错误" 服务器返回异常 检查 URL 是否有效
"未能创建 SSL/TLS 安全通道" 证书验证失败 使用 -SkipCertificateCheck
"请求被中止:未能创建 SSL/TLS 安全通道" 协议版本不支持 更新 .NET Framework
"无法连接到远程服务器" 网络不通 检查网络连接和防火墙

总结

解决 PowerShell irm 报错"基础连接已经关闭"的问题,推荐以下步骤:

快速修复(临时)

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
irm https://example.com/script.ps1

永久修复

  1. 将上述命令添加到 $PROFILE
  2. 或者通过注册表全局启用 TLS 1.2
  3. 或者安装最新版 PowerShell

替代方案

如果 PowerShell 修复无效,直接使用 curl.exe 替代。

建议:对于 Windows 7/8/Server 2012 等旧系统,推荐方法三(注册表全局启用),一劳永逸。对于 Windows 10/11,大多数情况下方法一就足够了。