I want to download the SSL certificate from, say https://www.outlook.com, using PowerShell. Is it possible? Could someone help me?
asked Mar 6, 2014 at 19:07 RafaMarrara RafaMarrara 831 1 1 gold badge 6 6 silver badges 8 8 bronze badgesTo share more knowledge :-)
$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"
My co-worker Michael J. Lyons shared this with me.
answered Mar 7, 2014 at 13:36 RafaMarrara RafaMarrara 831 1 1 gold badge 6 6 silver badges 8 8 bronze badgesNit: Powershell happily converts strings to enum members, so $cert.Export("Cert") is a valid thing to write.
Commented Jan 26, 2017 at 20:32 how do I get the root cert or chain with that? Commented Dec 21, 2018 at 18:53@Tilo a certificate is only considered valid if the client already has access to (and trusts) the root certificate. Passing the chain over the wire would be superfluous. However, if you convert the cert to an X509Certificate2, you do get a lot more information. Something like New-Object -TypeName "System.Security.Cryptography.X509Certificates.X509Certificate2" -ArgumentList $cert should do.
Commented Jul 12, 2019 at 20:24This solution only works if the certificate is considered valid. Otherwise the web request fails with an SSL error and the Certificate property is not populated.
Commented Nov 7, 2022 at 22:12The above example doesn't work for me in PowerShell 7 as $webRequest.ServicePoint.Certificate is null.
Commented Oct 18, 2023 at 19:43You should be able to get the public key by using the ServicePoint property on the HttpWebRequest object. This necessary information will be populated once we have made a http request to the site in question.
If the request is made to a site which has an untrusted certificate the GetResponse method will throw an exception, However, the ServicePoint will still contain the Certificate so we want to ensure we ignore WebException if the status is a trust failure.
So something like the following should work:
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"
If you want to call the method many times and some might have the same address, you might want to improve the function by using the ServicePointManager.FindServicePoint(System.Uri) method, since it will return a cached version if a request has already been made to that site. So you could check if the service point has been populated with information. If it hasn't, make the web request. If it has, just use the already existing information, saving yourself an http request.
answered Mar 6, 2014 at 21:56 Robert Westerlund Robert Westerlund 4,818 1 1 gold badge 21 21 silver badges 32 32 bronze badgesThis only displayed the int values of the public key, and Out-File contents were unusable. Answer from @RafaMarrara produces a valid .cert file and is in-line with OP's request of "download the SSL certificate"
Commented Nov 14, 2019 at 13:56function 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 > 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 < 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." >>
I successfully used these functions like this:
##Import self-signed certificate Get-WebsiteCertificate $baseUrl local.cer -trust | Out-Null Import-Certificate -certfile local.cer -SN Root | Out-Null
answered Mar 6, 2014 at 22:22
Andrew Savinykh Andrew Savinykh
26.1k 20 20 gold badges 107 107 silver badges 162 162 bronze badges
I've tried Get-WebsiteCertificate and the object returned only includes Issuer, Subject and a meaningless handle. Is it possible to get the expiry? EDIT: answering myself. if I use -OutFile and then load the file into a X509Certificate2 object, then I get a full suite of information including NotAfter, DnsNameList, etc.