Check-sslcert.ps1 (Updated Jan 15, 2013)
#Requires -version 2.0 param( [parameter(mandatory=$true,helpmessage="IP address or hostname to resolve remote system")][string]$ipaddr, [parameter(mandatory=$true,helpmessage="TCP port number that SSL application is listening on")][int]$port, [parameter(helpmessage="Hostname on certificate")][string]$myhostname=$ipaddr, [parameter(helpmessage="Verbose")][alias('fulldetail')][switch]$V ) function stripcomma([string]$tempstring) { write-debug "In Function StripComma $($tempstring)" return $tempstring.replace(',',';') } function convertoid([string]$oid) { write-debug "In function ConvertToOID: $($oid)" #strip off oid component common to all crypto types $oidstr = $oid.replace("1.2.840.113549.1.","") #pull out first number $firstval = $oidstr.substring(0,$oidstr.indexof('.')) #pull out second number for more detail $sub = $oidstr.substring(2) if ($sub.indexof('.') -gt 0) { $sub = $sub.substring(0,$sub.indexof('.')) } if ($firstval -eq "1") { $format = "PKCS-1" switch ($sub) { "1" { return ($format + " RSA Encryption") } "2" { return ($format + " MD2 with RSA") } "3" { return ($format + " rsadsi md4 with RSA")} "4" { return ($format + " MD5 with RSA") } "5" { return ($format + " SHA-1 with RSA") } "6" { return ($format + " rsaOAEPEncryptionSet")} "11" { return ($format + " sha256 with RSA") } } } elseif ($firstval -eq "5") { $format = "RSA PKCS5" switch ($sub) { "1" { return ($format + " rsadsi pbe with MD2 DES-CBC")} "3" { return ($format + " rsadsi pbe with MD5 DES-CBC")} "4" { return ($format + " pbe with MD2 and RC2_CBC")} "6" { return ($format + " pbe with MD5 and RC2_CBC")} "9" { return ($format + " pbe with MD5 and XOR")} "10" { return ($format + " pbe with SHA1 and DES-CBC")} "11" { return ($format + " pbe with SHA1 and RC2_CBC")} "12" { return ($format + " id-PBKDF2 key derivation function")} "13" { return ($format + " id-PBES2 PBES2 encryption")} "14" { return ($format + " id-PBMAC1 message auth scheme")} } } elseif ($firstval -eq "7" ) { $format = "PKCS-7" switch ($sub) { "1" { return ($format + " data")} "2" { return ($format + " signed data")} "3" { return ($format + " enveloped data")} "4" { return ($format + " signed and enveloped data")} "5" { return ($format + " digested data")} "6" { return ($format + " encrypted data")} } } elseif ($firstval -eq "12") { return ("PKCS-12") } elseif ($firstval -eq "15") { return ("PKCS-15") } else { return $oid } } ###### #MAIN# ###### #open TCP connection try { $conn = new-object system.net.sockets.tcpclient($ipaddr,$port) try { #create ssl stream on existing tcp connection $stream = new-object system.net.security.sslstream($conn.getstream()) #send hostname on cert to try SSL negotiation $stream.authenticateasclient($myhostname) $cert = $stream.get_remotecertificate() $cert2 = New-Object system.security.cryptography.x509certificates.x509certificate2($cert) #can get much more information with this class $validto = [datetime]::Parse($cert.getexpirationdatestring()) $validfrom = [datetime]::Parse($cert.geteffectivedatestring()) if ($V) { new-object psobject -property @{ Connection = "Success" Machine = $ipaddr CertFormat = ($cert.getformat()) CertExpiration = $validto CertIssueDate = $validfrom CertIssuer = ($cert.get_issuer()) SerialNumber = ($cert.getserialnumberstring()) CertSubject = (stripcomma $cert.get_subject()) CertType = (convertoid $cert.getkeyalgorithm()) } } else { #non verbose New-Object psobject -Property @{ Connection = "Success" Machine = $ipaddr CertExpiration = $validto } } } catch { #if SSL connection failed, cert may be invalid or name on cert didn't match, fails either way throw $_ } finally { Write-Debug "In finally: closing connection" $conn.close() } } catch { Write-Verbose "Error occurred connecting to $($ipaddr)" New-Object PSObject -Property @{ Machine = $ipaddr Connection = "Failure" Status = $_.exception.innerexception.message } }
This was helpful thanks for posting it! I ended up just using import-csv to parse a csv file and check those entries with a call to your script.
ReplyDeleteI also have to figure out how to use code boxes on my page like you do, that's much cleaner.
Thanks again!
I tried this script but I always end up in the try/catch section.
ReplyDeleteThe script seems to break at
$stream.authenticateasclient($myhostname)
Has anybody an idea how to exactly call this script
.\Check-sslcert.ps1 server.mydomain.com 443 mypcname.mydomain.com -fulldetail:$true
Help appreciated....
Hi Oliver,
ReplyDeleteI haven't done a great job of naming the parameters for the script. Maybe I'll get back to it and try to think of better names. The Help Message for each should cover it in more detail. Basically
-ipaddr is any IP or computer name (that will resolve in dns or other method) in order to open the TCP connection to a remote machine
-Port is the TCP port number that the SSL enabled application is listening on (remote machine)
-myhostname is the hostname on the certificate. Sometimes this does not machine the remote machine name (-ipaddr), for example, if you have two nodes of a load balanced web application and you wanted to check each node individually. The certificate may have the name of a load balancer DNS alias instead of the specific node name.
Hope this helps
For a quick example demo:
ReplyDeletePS C:\Users\nathan> .\check-sslcert.ps1 -ipaddr www.google.com -port 443 -myhostname
www.google.com
Connection CertExpiration Machine
---------- -------------- -------
Success 8/6/2013 3:43:27 AM www.google.com
PS C:\Users\nathan> .\check-sslcert.ps1 -ipaddr www.google.com -port 443 -myhostname
www.google.com -v
Machine : www.google.com
SerialNumber : 188DF90B00000000780B
CertSubject : CN=www.google.com; O=Google Inc; L=Mountain View;
S=California; C=US
CertExpiration : 8/6/2013 3:43:27 AM
CertIssueDate : 3/1/2013 8:15:52 PM
CertFormat : X509
CertType : PKCS-1 RSA Encryption
Connection : Success
CertIssuer : CN=Google Internet Authority, O=Google Inc, C=US
PS C:\Users\nathan> .\check-sslcert.ps1 -ipaddr www.google.com -port 443 -v
Machine : www.google.com
SerialNumber : 188DF90B00000000780B
CertSubject : CN=www.google.com; O=Google Inc; L=Mountain View;
S=California; C=US
CertExpiration : 8/6/2013 3:43:27 AM
CertIssueDate : 3/1/2013 8:15:52 PM
CertFormat : X509
CertType : PKCS-1 RSA Encryption
Connection : Success
CertIssuer : CN=Google Internet Authority, O=Google Inc, C=US
(when myhostname param is not provided the script uses the -ipaddr param as the hostname on the cert. Used for when the machine name and certification host name both match)
And just a few extra examples of where the hostname and name on certificate may not match. Dell.com has an SSL cert with many subject alternate names for it to validation true on. Here is an example with a valid SAN, and one that isn't there:
ReplyDeletePS C:\Users\nathan> .\check-sslcert.ps1 -ipaddr www.d
ell.com -port 443 -myhostname i.dell.com
Connection CertExpiration Machine
---------- -------------- -------
Success 22/3/2013 3:28:46 AM www.dell.com
PS C:\Users\nathan> .\check-sslcert.ps1 -ipaddr www.d
ell.com -port 443 -myhostname server.dell.com
Connection Status Machine
---------- ------ -------
Failure The remote certificate ... www.dell.com
Nathan,
ReplyDeleteHow can I determine public key length? For instance we're upgrading all of our certs to 2048-bit. How can I verify compliance?
Thanks,
Hank
Great job Nathan. Oliver thanks for asking; I appreciated the examples too.
ReplyDeleteFor public key length add to the verbose section under if ($V)
ReplyDeleteCertKeySize = $cert2.PublicKey.key.KeySize