Reputation: 24410
A number of sites offer tools to help check whether a certificate deployed to a site is valid; including checking that the full chain is installed, rather than just the client certificate. Some examples below:
I'd like to mimic this check via a PowerShell script (note: the aim is to check third party sites; so we don't have access to anything server side), so came up with the below:
Function Get-CertificateChain {
[OutputType([System.Security.Cryptography.X509Certificates.X509ChainElement])]
[CmdletBinding()]
Param (
[Parameter(Mandatory)]
[string]$ComputerName
,
[Parameter()]
[Int32]$Port = 443
,
[Parameter()]
[System.Security.Authentication.SslProtocols]$SslProtocol = [System.Security.Authentication.SslProtocols]::Tls12 # NB: The enum value Default is considered deprecated
,
[Parameter()]
[Switch]$CertificateInfoOnly
)
[System.Net.Sockets.Socket]$socket = [System.Net.Sockets.Socket]::new([System.Net.Sockets.SocketType]::Stream, [System.Net.Sockets.ProtocolType]::Tcp)
$socket.Connect($ComputerName, $Port)
try {
[System.Net.Sockets.NetworkStream]$networkStream = [System.Net.Sockets.NetworkStream]::new($socket, $true)
[System.Net.Security.SslStream]$sslStream = [System.Net.Security.SslStream]::new($networkStream, $true)
$sslStream.AuthenticateAsClient( $ComputerName, $null, $SslProtocol, $false )
[System.Security.Cryptography.X509Certificates.X509Certificate2]$remoteCertificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]($sslStream.RemoteCertificate)
[System.Security.Cryptography.X509Certificates.X509Chain]$chain = [System.Security.Cryptography.X509Certificates.X509Chain]::new()
$chain.Build($remoteCertificate) | Out-Null
Write-Verbose "Chain status length is: $($chain.ChainStatus.Length)" # Gives 0 every time :/
foreach ($chainElement in $chain.ChainElements) {
#[System.Security.Cryptography.X509Certificates.X509ChainElement]
if ($CertificateInfoOnly.IsPresent) {
$chainElement | Select-Object -ExpandProperty Certificate
} else {
$chainElement
}
}
} finally {
$socket.Close()
}
}
However, this retrieves the full chain even on sites which are missing the full chain; I guess because BUILD is overly helpful and fetches any missing intermediate or root certificates that it can find.
I found info on ChainStatus in a similar answer, which seems a better solution; however, when I call $chain.ChainStatus.Length
after Build is called (see Write-Verbose
call) I get a result of 0
.
What's the correct way to see whether a certificate chain being returned by a third party is valid (i.e. including on clients which don't automatically resolve missing or out of order certificate chains), or to get the information on which certificates are actually returned, using the .net framework?
Upvotes: 2
Views: 753
Reputation: 33088
Unfortunately, you can't (with SslStream). .NET will use a combination of local caches and network retrieval to try to complete the chain, and there's not a place you can really stop it (plus, you generally want it to put the root on top, since the normally-considered-correct TLS Server configuration is the entire chain except for the root authority).
Since .NET has TCP socket support you could do it in .NET by writing the TLS handshake code yourself, to then see what was actually on the wire... but I don't really advise that.
Upvotes: 1