从今年2月份开始,Github移除了一些弱的加密算法(参考GitHub Engineering - Weak cryptographic standards removal notice),导致的一个结果是,现在通过HTTPS访问Github,必须要有TLS1.2的支持。那PowerShell如何支持TLS1.2呢,请继续阅读。

微软从DotNet Framework 4.5才开始支持TLS1.2,Windows 7上默认的Powershell是2.0版本,能使用的DotNet Framework版本最高只到4.0,所以是不支持TLS1.2的。 如果使用Powershell去访问GitHub的Api,例如https://api.github.com/repos/PowerShell/PowerShell/releases/latest,会出现具体原因不详的连接被关闭的错误。示例代码如下:

(New-Object Net.WebClient).DownloadString('https://api.github.com/repos/PowerShell/PowerShell/releases/latest')

会出现以下错误:

Exception calling "DownloadString" with "1" argument(s): "The underlying connec
tion was closed: An unexpected error occurred on a send."
At line:1 char:49
+ (New-Object Net.WebClient).DownloadString('https://api.github.com/rep ...
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

为了解决这个问题,首先要将Powershell升级。通过Chocolatey,你可以通过以下命令把Powershell升级到5.1:

choco install powershell

需要在有管理员权限的Cmd里面运行上面的命令才可以,安装完之后需要重启电脑。

在升级后的PowerShell中运行:

(New-Object Net.WebClient).DownloadString('https://api.github.com/repos/PowerShell/PowerShell/releases/latest')

会有这样的错误提示:

Exception calling "DownloadString" with "1" argument(s): "The request was aborted: Could not create SSL/TLS secure channel."
At line:1 char:1
+ (New-Object Net.WebClient).DownloadString('https://api.github.com/rep ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException

检查一下[Net.ServicePointManager]::SecurityProtocol,发现TLS1.2并不在其中,只有SSL3和TLS:

> [Net.ServicePointManager]::SecurityProtocol 
Ssl3, Tls

查看[Net.SecurityProtocolType]::Tls12,可以发现TLS1.2是被当前的DotNet支持的,于是可以通过以下代码添加对TLS1.2的支持:

[Net.ServicePointManager]::SecurityProtocol = 
  [Net.SecurityProtocolType]::Tls12 -bor `
  [Net.SecurityProtocolType]::Tls11 -bor `
  [Net.SecurityProtocolType]::Tls -bor `
  [Net.SecurityProtocolType]::Ssl3

上面的代码可以简写成:

[Net.ServicePointManager]::SecurityProtocol = "Tls12, Tls11, Tls, Ssl3"

或者

[System.Net.ServicePointManager]::SecurityProtocol = @("Tls12","Tls11","Tls","Ssl3")

于是再次运行:

(New-Object Net.WebClient).DownloadString('https://api.github.com/repos/PowerShell/PowerShell/releases/latest')

发现出了一个新的错误:

Exception calling "DownloadString" with "1" argument(s): "The server committed a protocol violation. 
Section=ResponseStatusLine"
At line:1 char:1
+ (New-Object Net.WebClient).DownloadString('https://api.github.com/rep ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException
 

通过一番调查,发现GitHub的服务器对HTTP请求的字段有要求,必须得有user-agent字段,所以调整一下代码:

$wc = New-Object System.Net.WebClient
$wc.Headers.Add("user-agent", "anything")
$wc.DownloadString('https://api.github.com/repos/PowerShell/PowerShell/releases/latest')

调用成功,获得了正常的HTTP响应。

后记

  • 其实可以不用使用System.Net.WebClient,而直接使用高版本的PowerShell中带的Invoke-WebRequest这个Cmdlet来读取GitHub Api。这样的话就不需要设置user-agent字段了。

参考:

(完)