如何使用PowerShell从网站下载SSL证书?

问题描述 投票:0回答:5

我想使用 PowerShell 从 https://www.outlook.com 下载 SSL 证书。 是否可以?有人可以帮助我吗?

powershell ssl ssl-certificate
5个回答
48
投票

分享更多知识:-)

$webRequest = [Net.WebRequest]::Create("https://www.outlook.com")
try { $webRequest.GetResponse() } catch {}
$cert = $webRequest.ServicePoint.Certificate
$bytes = $cert.Export([Security.Cryptography.X509Certificates.X509ContentType]::Cert)
set-content -value $bytes -encoding byte -path "$pwd\Outlook.Com.cer"

我的同事 Michael J. Lyons 与我分享了这个。


14
投票

您应该能够通过使用

ServicePoint
对象上的
HttpWebRequest
属性来获取公钥。一旦我们向相关网站发出 http 请求,就会填充这些必要的信息。

如果向具有不受信任证书的站点发出请求,则 GetResponse 方法将引发异常,但是,

ServicePoint
仍将包含
Certificate
,因此我们要确保在状态为 a 时忽略
WebException
信任失败。

所以像下面这样的东西应该有效:

function Get-PublicKey
{
    [OutputType([byte[]])]
    PARAM (
        [Uri]$Uri
    )

    if (-Not ($uri.Scheme -eq "https"))
    {
        Write-Error "You can only get keys for https addresses"
        return
    }

    $request = [System.Net.HttpWebRequest]::Create($uri)

    try
    {
        #Make the request but ignore (dispose it) the response, since we only care about the service point
        $request.GetResponse().Dispose()
    }
    catch [System.Net.WebException]
    {
        if ($_.Exception.Status -eq [System.Net.WebExceptionStatus]::TrustFailure)
        {
            #We ignore trust failures, since we only want the certificate, and the service point is still populated at this point
        }
        else
        {
            #Let other exceptions bubble up, or write-error the exception and return from this method
            throw
        }
    }

    #The ServicePoint object should now contain the Certificate for the site.
    $servicePoint = $request.ServicePoint
    $key = $servicePoint.Certificate.GetPublicKey()
    Write-Output $key
}

Get-PublicKey -Uri "https://www.bing.com"
Get-PublicKey -Uri "https://www.facebook.com"

如果您想多次调用该方法并且有些可能具有相同的地址,您可能需要使用

ServicePointManager.FindServicePoint(System.Uri)
方法来改进该功能,因为如果已经向该站点发出请求,它将返回缓存的版本。因此您可以检查服务点是否已填充信息。如果没有,请发出网络请求。如果有,只需使用已经存在的信息,为自己节省一个 http 请求。


3
投票

来自http://poshcode.org/2521

function Get-WebsiteCertificate {
  [CmdletBinding()]
  param (
    [Parameter(Mandatory=$true)] [System.Uri]
      $Uri,
    [Parameter()] [System.IO.FileInfo]
      $OutputFile,
    [Parameter()] [Switch]
      $UseSystemProxy,  
    [Parameter()] [Switch]
      $UseDefaultCredentials,
    [Parameter()] [Switch]
      $TrustAllCertificates
  )
  try {
    $request = [System.Net.WebRequest]::Create($Uri)
    if ($UseSystemProxy) {
      $request.Proxy = [System.Net.WebRequest]::DefaultWebProxy
    }

    if ($UseSystemProxy -and $UseDefaultCredentials) {
      $request.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
    }

    if ($TrustAllCertificates) {
      # Create a compilation environment
      $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
      $Compiler=$Provider.CreateCompiler()
      $Params=New-Object System.CodeDom.Compiler.CompilerParameters
      $Params.GenerateExecutable=$False
      $Params.GenerateInMemory=$True
      $Params.IncludeDebugInformation=$False
      $Params.ReferencedAssemblies.Add("System.DLL") > $null
      $TASource=@'
        namespace Local.ToolkitExtensions.Net.CertificatePolicy {
          public class TrustAll : System.Net.ICertificatePolicy {
            public TrustAll() { 
            }
            public bool CheckValidationResult(System.Net.ServicePoint sp,
              System.Security.Cryptography.X509Certificates.X509Certificate cert, 
              System.Net.WebRequest req, int problem) {
              return true;
            }
          }
        }
'@ 
      $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
      $TAAssembly=$TAResults.CompiledAssembly

      ## We now create an instance of the TrustAll and attach it to the ServicePointManager
      $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
      [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
    }

    $response = $request.GetResponse()
    $servicePoint = $request.ServicePoint
    $certificate = $servicePoint.Certificate

    if ($OutputFile) {
      $certBytes = $certificate.Export(
          [System.Security.Cryptography.X509Certificates.X509ContentType]::Cert
        )
      [System.IO.File]::WriteAllBytes( $OutputFile, $certBytes )
      $OutputFile.Refresh()
      return $OutputFile
    } else {
      return $certificate
    }
  } catch {
    Write-Error "Failed to get website certificate. The error was '$_'."
    return $null
  }

  <#
    .SYNOPSIS
      Retrieves the certificate used by a website.

    .DESCRIPTION
      Retrieves the certificate used by a website. Returns either an object or file.

    .PARAMETER  Uri
      The URL of the website. This should start with https.

    .PARAMETER  OutputFile
      Specifies what file to save the certificate as.

    .PARAMETER  UseSystemProxy
      Whether or not to use the system proxy settings.

    .PARAMETER  UseDefaultCredentials
      Whether or not to use the system logon credentials for the proxy.

    .PARAMETER  TrustAllCertificates
      Ignore certificate errors for certificates that are expired, have a mismatched common name or are self signed.

    .EXAMPLE
      PS C:\> Get-WebsiteCertificate "https://www.gmail.com" -UseSystemProxy -UseDefaultCredentials -TrustAllCertificates -OutputFile C:\gmail.cer

    .INPUTS
      Does not accept pipeline input.

    .OUTPUTS
      System.Security.Cryptography.X509Certificates.X509Certificate, System.IO.FileInfo
  #>
}

function Import-Certificate {
<#
    .SYNOPSIS
        Imports certificate in specified certificate store.

    .DESCRIPTION
        Imports certificate in specified certificate store.

    .PARAMETER  CertFile
        The certificate file to be imported.

    .PARAMETER  StoreNames
        The certificate store(s) in which the certificate should be imported.

    .PARAMETER  LocalMachine
        Using the local machine certificate store to import the certificate

    .PARAMETER  CurrentUser
        Using the current user certificate store to import the certificate

    .PARAMETER  CertPassword
        The password which may be used to protect the certificate file

    .EXAMPLE
        PS C:\> Import-Certificate C:\Temp\myCert.cer

        Imports certificate file myCert.cer into the current users personal store

    .EXAMPLE
        PS C:\> Import-Certificate -CertFile C:\Temp\myCert.cer -StoreNames my

        Imports certificate file myCert.cer into the current users personal store

    .EXAMPLE
        PS C:\> Import-Certificate -Cert $certificate -StoreNames my -StoreType LocalMachine

        Imports the certificate stored in $certificate into the local machines personal store 

    .EXAMPLE
        PS C:\> Import-Certificate -Cert $certificate -SN my -ST Machine

        Imports the certificate stored in $certificate into the local machines personal store using alias names

    .EXAMPLE
        PS C:\> ls cert:\currentUser\TrustedPublisher | Import-Certificate -ST Machine -SN TrustedPublisher

        Copies the certificates found in current users TrustedPublishers store to local machines TrustedPublisher using alias  

    .INPUTS
        System.String|System.Security.Cryptography.X509Certificates.X509Certificate2, System.String, System.String

    .OUTPUTS
        NA

    .NOTES
        NAME:      Import-Certificate
        AUTHOR:    Patrick Sczepanksi (Original anti121)
        VERSION:   20110502
        #Requires -Version 2.0
    .LINK
        http://poshcode.org/2643
        http://poshcode.org/1937 (Link to original script)

#>

    [CmdletBinding()]
    param
    (
        [Parameter(ValueFromPipeline=$true,Mandatory=$true, Position=0, ParameterSetName="CertFile")]
        [System.IO.FileInfo]
        $CertFile,

        [Parameter(ValueFromPipeline=$true,Mandatory=$true, Position=0, ParameterSetName="Cert")]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]
        $Cert,

        [Parameter(Position=1)]
        [Alias("SN")]
        [string[]] $StoreNames = "My",

        [Parameter(Position=2)]
        [Alias("Type","ST")]
        [ValidateSet("LocalMachine","Machine","CurrentUser","User")]
        [string]$StoreType = "CurrentUser",

        [Parameter(Position=3)]
        [Alias("Password","PW")]
        [string] $CertPassword
    )

    begin
    {
        [void][System.Reflection.Assembly]::LoadWithPartialName("System.Security")
    }

    process 
    {
        switch ($pscmdlet.ParameterSetName) {
            "CertFile" {
                try {
                    $Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $($CertFile.FullName),$CertPassword
                }
                catch {   
                    Write-Error ("Error reading '$CertFile': $_ .") -ErrorAction:Continue
                }
            }
            "Cert" {

            }
            default {
                Write-Error "Missing parameter:`nYou need to specify either a certificate or a certificate file name."
            }
        }

        switch -regex ($storeType) {
            "Machine$" { $StoreScope = "LocalMachine" }
            "User$"  { $StoreScope = "CurrentUser" }
        } 

        if ( $Cert ) {
            $StoreNames | ForEach-Object {
                $StoreName = $_
                Write-Verbose " [Import-Certificate] :: $($Cert.Subject) ($($Cert.Thumbprint))"
                Write-Verbose " [Import-Certificate] :: Import into cert:\$StoreScope\$StoreName"

                if (Test-Path "cert:\$StoreScope\$StoreName") {
                    try
                    {
                        $store = New-Object System.Security.Cryptography.X509Certificates.X509Store $StoreName, $StoreScope
                        $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
                        $store.Add($Cert)
                        if ( $CertFile ) {
                            Write-Verbose " [Import-Certificate] :: Successfully added '$CertFile' to 'cert:\$StoreScope\$StoreName'."
                        } else {
                            Write-Verbose " [Import-Certificate] :: Successfully added '$($Cert.Subject) ($($Cert.Thumbprint))' to 'cert:\$StoreScope\$StoreName'."
                        }
                    }
                    catch
                    {
                        Write-Error ("Error adding '$($Cert.Subject) ($($Cert.Thumbprint))' to 'cert:\$StoreScope\$StoreName': $_ .") -ErrorAction:Continue
                    }
                    if ( $store ) {
                        $store.Close()
                    }
                } 
                else {
                    Write-Warning "Certificate store '$StoreName' does not exist. Skipping..."
                }
            }
        } else {
            Write-Warning "No certificates found."
        }
    }

    end { 
        Write-Host "Finished importing certificates." 
    }
}

我成功地使用了这些功能,如下所示:

##Import self-signed certificate
Get-WebsiteCertificate $baseUrl local.cer -trust | Out-Null
Import-Certificate -certfile local.cer -SN Root  | Out-Null

3
投票

@RafaMarrara 的答案对我来说非常有效,但有一个澄清:您无法从 .NET Core 应用程序执行此操作,因此我必须打开 PowerShell 5.1 才能运行它。在 PowerShell 7 中运行此命令会导致 Certificate 属性始终为空。

其原因是 .NET Core 放弃了对 ServicePointManager 和 ServicePoint 类的支持(参考)。


0
投票

@FireFlying,这是一个适用于基于 .NET Core 的 PowerShell 版本的解决方案。

第一部分是每个控制台或脚本运行一次

$csSource = @"
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace My.PSUtils.Net
{
  public class Validator {  
    public static bool SkipCertificateCheckValidator(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
      return true;
    }
  }
}
"@
$appDomainHasAssembly = ([System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { @($_.ExportedTypes | ForEach-Object { $_.FullName }) -Contains "My.PSUtils.Net" }).Count -gt 0
if (-not $appDomainHasAssembly) {  
  Add-Type -ReferencedAssemblies @() -TypeDefinition $csSource -Language CSharp -WarningAction SilentlyContinue
}

它创建一个回调来覆盖 .NET 默认执行的证书检查。

然后使用 TCPClient 检索证书,如果需要,这部分可以包含在循环中:

$uriBuilder = [System.UriBuilder]::New("https://www.outlook.com")
$client = [System.Net.Sockets.TcpClient]::new($uriBuilder.Uri.Host, $uriBuilder.Uri.Port)
$stream = [System.Net.Security.SslStream]::new($client.GetStream(), $true, [My.PSUtils.Net.Validator]::SkipCertificateCheckValidator)
$stream.AuthenticateAsClient($uriBuilder.Uri.Host) | Out-Null
$certificate = $stream.RemoteCertificate
$stream.Close()
$stream.Dispose()
$certificate | Format-List Subject,NotBefore,NotAfter,DnsNameList

在 Windows 上的 PS v7.3.10 上测试成功。

希望有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.