Show SMTP Certificate of Remote Server with PowerShell

Hallo zusammen,

Vor einiger Zeit habe ich gebloggt, wie man ein Zertifikat von einem Server mit Openssl prüfen kann. Nun habe ich etwas ähnliches mit PowerShell realisiert.

Dafür habe ich mir grosse Teile des Codes von Glen Scales geliehen https://github.com/gscales/Powershell-Scripts/blob/master/TLS-SMTPMod.ps1

###############################################################################
# Connect to SMTP Server, check for STARTTLS and then get the Certificate
# 29.06.2021 V1.0 Andres Bohren - Initial Version
###############################################################################
<#
.SYNOPSIS
  
.DESCRIPTION
    Connect to SMTP Server, check for STARTTLS and then get the Certificate

.PARAMETER ServerName
    The Servername of the SMTP Server

.PARAMETER Port
    The Port of the SMTP Server (25 / 587)

.PARAMETER Sendingdomain
    The Sendingdomain used in the EHLO

.PARAMETER CertificateFilePath
    Optional a Path to a File for saving the Certificate. Example: C:\GIT_WorkingDir\PowerShellScripts\cer.cer

.EXAMPLE
    .\Get-SMTPCertificate.ps1 -ServerName "icewolfch.mail.protection.outlook.com" -Port 25 -Sendingdomain "icewolf.ch" -CertificateFilePath "C:\GIT_WorkingDir\PowerShellScripts\cer.cer"

.LINK
#>


param (
    [parameter(Mandatory=$true)][String]$Port,
    [parameter(Mandatory=$true)][String]$ServerName,
    [parameter(Mandatory=$true)][String]$Sendingdomain,
    [parameter(Mandatory=$false)][String]$CertificateFilePath
)

#Connect
Write-Host("Connect $ServerName $Port") -ForegroundColor Green
$socket = new-object System.Net.Sockets.TcpClient($ServerName, $Port)
$stream = $socket.GetStream()
$streamWriter = new-object System.IO.StreamWriter($stream)
$streamReader = new-object System.IO.StreamReader($stream)
$stream.ReadTimeout = 5000
$stream.WriteTimeout = 5000  
$streamWriter.AutoFlush = $true
$sslStream = New-Object System.Net.Security.SslStream($stream)
$sslStream.ReadTimeout = 5000
$sslStream.WriteTimeout = 5000       
$ConnectResponse = $streamReader.ReadLine();
Write-Host($ConnectResponse)
if(!$ConnectResponse.StartsWith("220")){
    throw "Error connecting to the SMTP Server"
}

#Send "EHLO"
Write-Host(("EHLO " + $Sendingdomain)) -ForegroundColor Green
$streamWriter.WriteLine(("EHLO " + $Sendingdomain));

$response = @()
Try {
    while($streamReader.EndOfStream -ne $true)
    {
        $ehloResponse = $streamReader.ReadLine();
        Write-Host($ehloResponse)
        $response += $ehloResponse
    }
} catch {

    If ($response -match "STARTTLS")
    {
        #StartTLS found
        Write-Host("STARTTLS") -ForegroundColor Green
        $streamWriter.WriteLine("STARTTLS");
        $startTLSResponse = $streamReader.ReadLine();
        Write-Host($startTLSResponse)

        #Get Certificate
        $ccCol = New-Object System.Security.Cryptography.X509Certificates.X509CertificateCollection
        $sslStream.AuthenticateAsClient($ServerName,$ccCol,[System.Security.Authentication.SslProtocols]::Tls12,$false)    
        $Cert = $sslStream.RemoteCertificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)
        If ($Null -ne $CertificateFilePath)
        {
            [System.IO.File]::WriteAllBytes($CertificateFilePath, $Cert)
            Write-Host("File written to " + $CertificateFilePath)
        }

        #Show Certificate Details
        Write-Host ""
        Write-Host "Issuer: $($sslStream.RemoteCertificate.Issuer)"
        Write-Host "Subject: $($sslStream.RemoteCertificate.Subject)"
        Write-Host "ValidFrom: $($sslStream.RemoteCertificate.GetEffectiveDateString())"
        Write-Host "ValidTo: $($sslStream.RemoteCertificate.GetExpirationDateString())"
        Write-Host "SerialNumber: $($sslStream.RemoteCertificate.GetSerialNumberString())"
        Write-Host "Thumbprint: $($sslStream.RemoteCertificate.GetCertHashString())"

        #Convert to Base64
        Write-Host ""
        $StringBuilder = new-Object System.Text.StringBuilder
        [void]$StringBuilder.AppendLine("-----BEGIN CERTIFICATE-----");
        [void]$StringBuilder.AppendLine([System.Convert]::ToBase64String($cert,[System.Base64FormattingOptions]::InsertLineBreaks))
        [void]$StringBuilder.AppendLine("-----END CERTIFICATE-----")
        $CertString = $StringBuilder.Tostring()
        Write-Host "$CertString"

        $stream.Dispose()
        $sslStream.Dispose()

    } else {
        Write-Host "ERROR: No <STARTTLS> found" -ForegroundColor Red
    }

}

Das Script wird beispielsweise mit folgenden Parametern aufgerufen

 .\Get-SMTPCertificate.ps1 -ServerName "icewolfch.mail.protection.outlook.com" -Port 25 -Sendingdomain "icewolf.ch" -CertificateFilePath "C:\GIT_WorkingDir\PowerShellScripts\cer.cer"

Beim heruntergeladenen Zertifikat passt der CN aus dem Subject, der Issuer und das ValidFrom und ValidTo.

Auch die SerialNumber

und der Thumbprint stimmen mit der Ausgabe in der PowerShell überein

Grüsse
Andres Bohren