A time ago, Windows PKI team posted an article about a tool that allows you to check web server SSL certificate: Verifying The SSL Certificate Expiration with a tool. Unfortunately, the download link is broken. I have this tool and uploaded it to my weblog: VerifySSLCertificate.
The tool is very good, but what if you want to run the test against a bulk of servers? Any sort of automation and batching means some PowerShell stuff :). To provide administrators with such tool I wrote a PowerShell script, where you can test web server SSL certificate and it's status. You can export required fields to XML or CSV for future examination/audit. Let's go:
function Test-WebServerSSL { [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] [string]$URL, [Parameter(Position = 1)] [ValidateRange(1,65535)] [int]$Port = 443, [Parameter(Position = 2)] [Net.WebProxy]$Proxy, [Parameter(Position = 3)] [int]$Timeout = 15000, [switch]$UseUserContext ) Add-Type @" using System; using System.Net; using System.Security.Cryptography.X509Certificates; namespace PKI { namespace Web { public class WebSSLTest { public Uri OriginalURi { get; set; } public Uri ReturnedURi { get; set; } public X509Certificate2 Certificate { get; set; } //public X500DistinguishedName Issuer; //public X500DistinguishedName Subject; public string Issuer { get; set; } public string Subject { get; set; } public string[] SubjectAlternativeNames { get; set; } public bool CertificateIsValid { get; set; } //public X509ChainStatus[] ErrorInformation; public string[] ErrorInformation { get; set; } public HttpWebResponse Response { get; set; } } } } "@ $ConnectString = "https://$url`:$port" $WebRequest = [Net.WebRequest]::Create($ConnectString) $WebRequest.Proxy = $Proxy $WebRequest.Credentials = $null $WebRequest.Timeout = $Timeout $WebRequest.AllowAutoRedirect = $true [Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} try {$Response = $WebRequest.GetResponse()} catch {} if ($WebRequest.ServicePoint.Certificate -ne $null) { $Cert = [Security.Cryptography.X509Certificates.X509Certificate2]$WebRequest.ServicePoint.Certificate.Handle try {$SAN = ($Cert.Extensions | Where-Object {$_.Oid.Value -eq "2.5.29.17"}).Format(0) -split ", "} catch {$SAN = $null} $chain = New-Object Security.Cryptography.X509Certificates.X509Chain -ArgumentList (!$UseUserContext) [void]$chain.ChainPolicy.ApplicationPolicy.Add("1.3.6.1.5.5.7.3.1") $Status = $chain.Build($Cert) New-Object PKI.Web.WebSSLTest -Property @{ OriginalUri = $ConnectString; ReturnedUri = $Response.ResponseUri; Certificate = $WebRequest.ServicePoint.Certificate; Issuer = $WebRequest.ServicePoint.Certificate.Issuer; Subject = $WebRequest.ServicePoint.Certificate.Subject; SubjectAlternativeNames = $SAN; CertificateIsValid = $Status; Response = $Response; ErrorInformation = $chain.ChainStatus | ForEach-Object {$_.Status} } $chain.Reset() [Net.ServicePointManager]::ServerCertificateValidationCallback = $null } else { Write-Error $Error[0] } }
The code connects to a server by using provided host name, instantiates a SSL connection, retrieves actual SSL certificate and tests, whether the certificate is valid (full tests are performed, including revocation checking). The following parameters can be used:
Here are some examples:
[↓] [vPodans] Test-WebServerSSL signin.ebay.com OriginalURi : https://signin.ebay.com/ ReturnedURi : https://signin.ebay.com/ Certificate : [Subject] CN=signin.ebay.com, OU=Site Operations, O=eBay Inc., STREET=2145 Hamilton Ave, L=San Jose, S=California, PostalCode=95125, C=US, SERIALNUMBER=2871352, OID.2.5.4.15=Private Organization , OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US [Issuer] CN=VeriSign Class 3 Extended Validation SSL CA, OU=Terms of use at https://www.verisign.com /rpa (c)06, OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US [Serial Number] 3EA2CB6BAC5BDED626A5AEA6D914E30C [Not Before] 19.01.2011 2:00:00 [Not After] 24.01.2013 1:59:59 [Thumbprint] 9DB60558622A1FA3EF5C0683671751CFDA8C7253 Issuer : CN=VeriSign Class 3 Extended Validation SSL CA, OU=Terms of use at https://www.verisign.com/r pa (c)06, OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US Subject : CN=signin.ebay.com, OU=Site Operations, O=eBay Inc., STREET=2145 Hamilton Ave, L=San Jose, S= California, PostalCode=95125, C=US, SERIALNUMBER=2871352, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US SubjectAlternativeNames : {DNS Name=signin.ebay.com, DNS Name=signin.ebay.at, DNS Name=signin.ebay.be, DNS Name=signin. ebay.ca...} CertificateIsValid : True ErrorInformation : Response : System.Net.HttpWebResponse [↓] [vPodans] Test-WebServerSSL trust.beeline.ru OriginalURi : https://trust.beeline.ru/ ReturnedURi : https://trust.beeline.ru/ Certificate : [Subject] CN=trust.beeline.ru, OU=DIT, O=Vimpelcom, L=Moscow, S=Moscow, C=RU [Issuer] CN=Vimpelcom ExternalCA, O=Vimpelcom, C=RU [Serial Number] 40C9BF400000000341D9 [Not Before] 02.02.2012 8:32:51 [Not After] 02.02.2013 8:42:51 [Thumbprint] 58FEEEC8676F7E2D106DEA4235B22AFC09086AEB Issuer : CN=Vimpelcom ExternalCA, O=Vimpelcom, C=RU Subject : CN=trust.beeline.ru, OU=DIT, O=Vimpelcom, L=Moscow, S=Moscow, C=RU SubjectAlternativeNames : CertificateIsValid : False ErrorInformation : {PartialChain, RevocationStatusUnknown, OfflineRevocation} Response : System.Net.HttpWebResponse [↓] [vPodans]
In the output you will see some important details: certificate subject, issuer and Subject Alternative Name (SAN) extension names. The most important fields are bolded: CertificateIsValid displays whether the certificate has passed or failed all certificate checks. If at least one test fails — detailed error information will be set to ErrorInformation property. In the current example, the second certificate failed revocation checking (revocation information is unavailable) and has partial chain (the chain is missing one or more CA certificate and they are not downloadable/reachable). If the server has expired certificate, you will see the following information in these two properties:
CertificateIsValid : False ErrorInformation : {NotTimeValid}
The tool will return all certificate errors (not throwing only the first). Hope, you find the script helpful.
Why not use something like �Get-Certificate | Test-Certicate�? I bet you (or other guys) already have those functions. And if they don't work this way for some reason, it seems like a good opportunity to improve them, not reinventing the wheel.
because the code runs against remote web servers. In many cases the certificate locally may look as valid, but from client perspective � not. For example, certificate name mismatch, trust issues, revocation checking and so on.
So, why not make �Get-Certificate� so that it grabs it from those remove web servers? All as you did it here: Get-Certificate -Path "signin.ebay.com" -Port "443" -Proxy "someshithere.contoso.com" -ProxyPort "8080" -Timeout "100500" This should bring the remote certificate into the client-side perspective. Then pipe it to �Test-Certificate� which would obviously check it locally.
Currently I haven't a good design for universal Test-Certificate (it useless to have a separate Test-Certificate cmdlet for SSL certificates only). Probably, in future, when I will have a better solution.
why do you need a separate one? It should just accept output from Get-Certificate.
Currently I have no idea how to create a universal Test-Certificate command.
What is the input format your current implementation of Test-Certificate expects?
Clear Explanation.
THANK YOU! I have to sift through lots and lots of nodes and this is a LIFE Saver.
I have my own personal CA whose cert I have installed into the Trusted Root CAs. This way I can simulate the signing process and test configurations without the need for expensive commercial CA signed certs all the time. However, when I run your function against a server configured in this way, I get the following: CertificateIsValid : False ErrorInformation : {RevocationStatusUnknown} Not knowing much about SSL certs & revocation, is there any way I can configure things up so your function will generate a CertificateIsValid status of True for my test environments?
This is a big question. Revocation checking is one of the most (and complicated) subject in PKI. The most likely issue is that URLs in the certificate's CDP extension are not accessible from your client. Open the target certificate, and examine URLs in CRL Distribution Points extension.
I wish output valid from and valid to to this script. Any Ideas?
The script already outputs these values (as a part of the certificate). But you can add the following properties to object: public DateTime ValidFrom; public DateTime ValidTo; and in the code (within 'New-Object PKI.Web.WebSSL' hashtable) add the following lines: ValidFrom = $Cert.NotBefore; ValidTo = $Cert.NotAfter;
I tried adding "pubic DateTime ValidTo;" and "ValidTo = $Cert.NotAfter;" and I'm getting this error: Add-Type : Cannot add type. The type name 'PKI.Web.WebSSL' already exists. At C:\Temp\PowerShell\Test-SSL.ps1:16 char:9 + Add-Type <<<< @" + CategoryInfo : InvalidOperation: (PKI.Web.WebSSL:String) [Add-Type], Exception + FullyQualifiedErrorId : TYPE_ALREADY_EXISTS,Microsoft.PowerShell.Commands.AddTypeCommand New-Object : Member "ValidTo" not found for the given .NET object. At C:\Temp\PowerShell\Test-SSL.ps1:56 char:19 + New-Object <<<< PKI.Web.WebSSL -Property @{ + CategoryInfo : InvalidOperation: (:) [New-Object], InvalidOperationException + FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.NewObjectCommand FYI - renamed the function to Test-SSL. I'd like to be able to grab the URL, SANs, Issuer, and expiration. I can get all but the expiration.
I tried adding "pubic DateTime ValidTo;" and "ValidTo = $Cert.NotAfter;" and I'm getting this error: Add-Type : Cannot add type. The type name 'PKI.Web.WebSSL' already exists. At C:\Temp\PowerShell\Test-SSL.ps1:16 char:9 + Add-Type <<<< @" + CategoryInfo : InvalidOperation: (PKI.Web.WebSSL:String) [Add-Type], Exception + FullyQualifiedErrorId : TYPE_ALREADY_EXISTS,Microsoft.PowerShell.Commands.AddTypeCommand New-Object : Member "ValidTo" not found for the given .NET object. At C:\Temp\PowerShell\Test-SSL.ps1:56 char:19 + New-Object <<<< PKI.Web.WebSSL -Property @{ + CategoryInfo : InvalidOperation: (:) [New-Object], InvalidOperationException + FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.NewObjectCommand FYI - renamed the function to Test-SSL. I'd like to be able to grab the URL, SANs, Issuer, and expiration. I can get all but the expiration.
When you modified C# signature, you need to restart PowerShell console.
Thanks that did the trick! I really appreciate this script, it will help me tremendously!
Also I would like to advice you to try the PSPKI module: http://pspki.codeplex.com/ The module has an improved version of this command.
When I am trying to use it, it does not show anything on the screen, tried with and without -URL parameters
Any errors there? The function should return an exception or the formatted output.
Hi, I use this script to automatically verify certificate information in my environment. I've been able to use it to grab information in the certificate...but do you know how I might be able to get the OU value from the Subject string? Thanks in advance, Hank
You can use regular expressions to extract OU information. Though regex may be a bit complicated, because there may be multiple OU attributes. Or split subject name to tokens: $RDNs = (Test-WebServerSSL signin.ebay.com).Subject.Split(",").Trim() and index to $RDNs to get desired RDN attribute.
Seems the "https://$url`:$port" does not support the full uri path... For example... Redirects https:\\webserver - might redirect or produce a cert and https:\\webserver\folder - might redirect to a different place and produce a different cert in that case $ConnectString would produce https:\\webserver\folder:443 which produces a slew of errors... Exception calling "Create" with "1" argument(s): "Invalid URI: The hostname could not be parsed. At line:39 char:5 + $WebRequest = [Net.WebRequest]::Create($ConnectString) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : UriFormatException Not a show stopper and surely not the norm but thought I would let you know since I ran into it. Scott
> Seems the "https://$url`:$port" does not support the full uri path... yes, currently it is not supported. You can fill a bug here: https://pspki.codeplex.com/workitem/list/basic
Thanks for this script! I'm going to try and add some email functionality to it but it is awesome! Thanks again.
This is awesome! Thank you!!!!!!!!
I used your code to develop a tool which tests for the signature algorithm of a web server. Turns out SHA-1 is being phased out in November, so we needed to check approximately 500 websites. This made it much easier. Code available here if you want/need it. https://gist.github.com/derekmurawsky/1345e55699343ec4ea9c Thanks!
PS C:\> Test-WebServerSSL ebay.com Exception calling "SendRequest" with "0" argument(s): "Object reference not set to an instance of an object." At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\PSPKI\Client\Test-WebServerSSL.ps1:26 char:2 + $Response.SendRequest() + ~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : NullReferenceException
I responded to you on CodePlex web site.
Hi, thanks for you code, but i prefer to use the windows tool - VerifySSLCertificate, the download link is borken, could you renew it ?
Your link to VerifySSLCertificate.zip is broken (404 not found).
Hi,
the script seems to be broken on Win10 1607 / Powershell5. I'n not familiar with using .NET Namespaces directly so I cannot fix this myself...
New-Object : The value supplied is not valid, or the property is read-only. Change the value, and then try again.
At line:54 char:9
+ New-Object PKI.Web.WebSSLTest -Property @{
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [New-Object], Exception
+ FullyQualifiedErrorId : SetValueException,Microsoft.PowerShell.Commands.NewObjectCommand
Please, open a report on issue tracker: https://pspki.codeplex.com/workitem/list/basic
For PowerShell 5 (and likely 3+) you could remove the add-type at the beginning and replace the new-object line by the following
<PoSHCode>
$ConnectionInformation = New-Object PSObject -Property ([Ordered]@{
OriginalUri = $ConnectString;
ReturnedUri = $Response.ResponseUri;
Certificate = [Security.Cryptography.X509Certificates.X509Certificate2]$WebRequest.ServicePoint.Certificate;
Issuer = $WebRequest.ServicePoint.Certificate.Issuer;
Subject = $WebRequest.ServicePoint.Certificate.Subject;
SubjectAlternativeNames = $SAN;
CertificateIsValid = $Status;
Response = $Response;
ErrorInformation = $chain.ChainStatus | ForEach-Object {$_.Status}
})
$ConnectionInformation.PSObject.TypeNames.Add("Indented.LDAP.ConnectionInformation")
$ConnectionInformation
</PoSHCode>
Don't yet understand how it works, just capturing out of http://www.indented.co.uk/2015/03/31/testing-ldaps
It appears that when a wildcard certificate is presented it assumes all subdomains as covered.
Example: Test-WebServerSSL -URL wrong.host.badssl.com
CertificateIsValid : True (Should be false)
Hello,
I´m not a programmer unf. could you help me on a code for check ssl based url to have certification autority name, data created and will expire,please?
I appreciate your help!
Test-WebServerSSL www.google.com
Test-WebServerSSL : Exception calling "GetResponse" with "0" argument(s): "The operation has timed out"
At line:1 char:1
+ Test-WebServerSSL www.google.com
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-WebServerSSL
Why with some web pages this works ok, but with others I'm getting response (let's say for non-valid certificates).
How can I check for all sites with valid and non-valid certificates?
Test-WebServerSSL : Exception calling "GetResponse" with "0" argument(s): "The operation has timed out"
At line:1 char:1
+ Test-WebServerSSL www.google.com
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-WebServerSSL
-or-
New-Object : The value supplied is not valid, or the property is read-only. Change the value, and then try again.
At line:54 char:9
+ New-Object PKI.Web.WebSSLTest -Property @{
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [New-Object], Exception
+ FullyQualifiedErrorId : SetValueException,Microsoft.PowerShell.Commands.NewObjectCommand
Commenting out ErrorInformation solved the error:
New-Object : The value supplied is not valid, or the property is read-only. Change the value, and then try again.
At C:\Users\bdoyle1\Test-WebServerSSL.ps1:54 char:9
+ New-Object PKI.Web.WebSSLTest -Property @{
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [New-Object], Exception
+ FullyQualifiedErrorId : SetValueException,Microsoft.PowerShell.Commands.NewObjectCommand
Alternately removing the "| ForEach-Object {$_.ChainStatus}" loop did the same.
The problem seems to be if the chain status is empty {} PS5 can't handle it.
To prevent throw error: "New-Object : The value supplied is not valid, or the property is read-only. Change the value, and then try again."
Change line:
ErrorInformation = $chain.ChainStatus | ForEach-Object {$_.Status}
To Line:
ErrorInformation = $(IF($chain.ChainStatus){$chain.ChainStatus | ForEach-Object {$_.Status}} Else {$NULL})
Post your comment:
Comments: