Um auf Informationen einer Zertifikatsantragsdatei zuzugreifen, bietet Windows leider keine Schnittstelle ab Werk an.
Es gibt beispielsweise die Möglichkeit, einen CSR in einen Base64-kodierten String umzuwandeln und anschließend wieder als Byte-Array zu dekodieren, was jedoch weder praktisch umzusetzen noch einfach zu lesen ist, da die dekodierten Strings unlesbare Zeichen enthalten können. Außerdem muss hierzu auf mehrere Abhängigkeiten referenziert und im besten Fall eine höhere Programmiersprache wie C# verwendet werden.
Um dennoch die in einer CSR-Datei enthaltenen Informationen programmiertechnisch weiterverwenden zu können, lässt sich dies mit folgender, einfach zu lesender und anzupassender Funktion bewerkstelligen, welches die CSR-Datei über Parsingoperationen in seine Bestandteile zerlegt.
Die Funktion beruft sich nur auf die beiden vorinstallierten Windows Standardprogramme certutil.exe sowie PowerShell und benötigt keinerlei weitere Bibliotheken oder Abhängigkeiten. Sie ist ausschließlich für das Einlesen von PKCS #10 Zertifikatsantragsdateien vorgesehen, da die Parsing-Logik sich an der Struktur dieses Typs orientiert. Sie ist nicht für das Einlesen von Zertifikaten vorgesehen, da hierfür bereits andere Tools und Schnittstellen existieren.
Die Kernkomponente der Funktion um den Inhalt aufzulösen bilden Regular Expressions, welche nach festgelegten Kriterien jede Zeile analysieren und feststellen, ob der Inhalt mit den Regeln übereinstimmt oder nicht. Ein Einlesen funktioniert sowohl in englischsprachigen als auch in deutschsprachigen Umgebungen.
Die Funktion
function ParseCSR{
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$CSRdatei,
[switch]$Ausgabe
)
#Main Hastabelle
$DecodedCSR=@{}
#Subject Hastabelle mit Unterarrays
$DecodedCSR[“Subject”] = @{}
$DecodedCSR[“Subject”][“CommonName”] = @()
$DecodedCSR[“Subject”][“Country”] = @()
$DecodedCSR[“Subject”][“DomainComponent”] = @()
$DecodedCSR[“Subject”][“Email”] = @()
$DecodedCSR[“Subject”][“GivenName”] = @()
$DecodedCSR[“Subject”][“Initials”] = @()
$DecodedCSR[“Subject”][“Location”] = @()
$DecodedCSR[“Subject”][“Organisation”] = @()
$DecodedCSR[“Subject”][“OrganisationUnit”] = @()
$DecodedCSR[“Subject”][“State”] = @()
$DecodedCSR[“Subject”][“Street”] = @()
$DecodedCSR[“Subject”][“Surname”] = @()
$DecodedCSR[“Subject”][“Title”] = @()
#SAN Hastabelle mit Unterarrays
$DecodedCSR[“SAN”] = @{}
$DecodedCSR[“SAN”][“DNS”] = @()
$DecodedCSR[“SAN”][“RFC822Name”] = @()
$DecodedCSR[“SAN”][“IPAddress”] = @()
$DecodedCSR[“SAN”][“URL”] = @()
$DecodedCSR[“SAN”][“PrincipalName”] = @()
#Sonstige Variablen als Arrays
$DecodedCSR[“SignatureAlgorithm”] = @()
$DecodedCSR[“PublicKeyAlgorithm”] = @()
$DecodedCSR[“PublicKeyLength”] = @()
#Datei einlesen
$certutilAusgabe = certutil $csrdatei
#Wenn Switch “Detailliert” ausgewählt ist, Ausgabe des Certutil Befehls in der Konsole anzeigen
if($Ausgabe){
foreach($Zeile in $certutilAusgabe){
Write-Host $Zeile
}
}
foreach($Zeile in $certutilAusgabe){
switch -Regex ($Zeile){
‘^[\s]+CN[\s]?=[\s]?’ {$DecodedCSR.Subject.CommonName += $Zeile -replace “^.*=”}
‘^[\s]+C[\s]?=[\s]?’ {$DecodedCSR.Subject.Country += $Zeile -replace “^.*=”}
‘^[\s]+DC[\s]?=[\s]?’ {$DecodedCSR.Subject.DomainComponent += $Zeile -replace “^.*=”}
‘^[\s]+E[\s]?=[\s]?’ {$DecodedCSR.Subject.Email += $Zeile -replace “^.*=”}
‘^[\s]+I[\s]?=[\s]?’ {$DecodedCSR.Subject.Initials += $Zeile -replace “^.*=”}
‘^[\s]+L[\s]?=[\s]?’ {$DecodedCSR.Subject.Location += $Zeile -replace “^.*=”}
‘^[\s]+O[\s]?=[\s]?’ {$DecodedCSR.Subject.Organisation += $Zeile -replace “^.*=”}
‘^[\s]+OU[\s]?=[\s]?’ {$DecodedCSR.Subject.OrganisationUnit += $Zeile -replace “^.*=”}
‘^[\s]+S[\s]?=[\s]?’ {$DecodedCSR.Subject.State += $Zeile -replace “^.*=”}
‘^[\s]+STREET[\s]?=[\s]?’ {$DecodedCSR.Subject.Street += $Zeile -replace “^.*=”}
‘^[\s]+SN[\s]?=[\s]?’ {$DecodedCSR.Subject.Surname += $Zeile -replace “^.*=”}
‘^[\s]+T[\s]?=[\s]?’ {$DecodedCSR.Subject.Title += $Zeile -replace “^.*=”}
‘^[\s]+DNS[\s-]Name[\s]?=[\s]?’ {$DecodedCSR.SAN.DNS += $Zeile -replace “^.*=” }
‘^[\s]+RFC822[\s-]Name[\s]?=[\s]?’ {$DecodedCSR.SAN.RFC822Name += $Zeile -replace “^.*=” }
‘^[\s]+IP-Adress[e]?[\s]?=[\s]?’ {$DecodedCSR.SAN.IPAddress += $Zeile -replace “^.*=” }
‘^[\s]+URL[\s]?=[\s]?’ {$DecodedCSR.SAN.URL += $Zeile -replace “^.*=” }
‘^[\s]*Länge des öffentlichen Schlüssels:|Public Key Length:’ {$DecodedCSR.PublicKeyLength = $Zeile -replace “[^0-9]+” }
‘Signaturalgorithmus:|Signature Algorithm:’ {$DecodedCSR.SignatureAlgorithm = $certutilAusgabe[($certutilAusgabe.IndexOf($Zeile)+1)] }
‘Öffentlicher Schlüssel-Algorithmus:|Public Key Algorithm:’ {$DecodedCSR.PublicKeyAlgorithm = $certutilAusgabe[($certutilAusgabe.IndexOf($Zeile)+1)] }
}
}
#Array mit allen Informationen zurückgeben
return $DecodedCSR
}
Verwendung
Syntax
$var = ParseCSR -CSRdatei dateiname.csr (erforderlich) -Ausgabe (optional)
Kurzfassung To-Go
#CSR einlesen und Objekte in $var ablegen
$var = ParseCSR -CSRdatei dateiname.csr
#Alternativ ohne Parameter “CSRdatei”
$var = ParseCSR dateiname.csr
#Auf Objekte in $var zugreifen
Write-Host $var.Subject.CommonName
Write-Host $var.SAN.IPAddress
#Zusätzlich zum Parsen die certutil Ausgabe mit in PowerShell ausgeben
$var = ParseCSR dateiname.csr -Ausgabe
Detaillierte Erklärung
Um auf die im CSR enthaltenen Objekte zugreifen zu können, muss die Funktion wie hier zu sehen in eine Variable geschrieben werden:
$result = ParseCSR “C:\Pfad\Zertifikatsantrag.csr”
Anschließend kann man über die Variable auf die untergeordneten Objekte wie folgt zugreifen:
$result.Subject.CommonName
#Ausgabe
test.pki.cloud
Die Objekte innerhalb der zurückgegebenen Variable orientieren sich an der Namensgebung der Windows Zertifikatsdienste und sind wie folgt strukturiert:
- Subject
- CommonName
- Country
- DomainComponent
- GivenName
- Initials
- Location
- Organisation
- OrganisationUnit
- State
- Street
- Surname
- Title
- Subject
- SAN
- DNS
- RFC822Name
- IPAddress
- URL
- PrincipalName
- SAN
- PublicKeyLength
- SignatureAlgorithm
- PublicKeyAlgorithm
Möchte man also auf die DNS-Einträge zugreifen, so tut man dies über “$result.SAN.DNS”.
Die Einträge SignatureAlgorithm und PublicKeyLength gehören weder zum Subject, noch zum SAN und sind deshalb direkt aufrufbar über $result.SignatureAlgorithm und “$result.PublicKeyLength”.
Zusätzlich gibt es den optionalen Parameter “-Ausgabe”, welcher sowohl mit oder ohne eine Variable verwendet werden kann.
Bei Verwendung dieses Parameters wird lediglich die Konsolenausgabe von Certutil mit in der PowerShell Konsole ausgegeben:
ParseCSR “C:\Pfad\Zertifikatsantrag.csr” -Ausgabe
#oder
$result = ParseCSR “C:\Pfad\Zertifikatsantrag.csr” -Ausgabe
Verweis auf Funktion
Die Funktion kann entweder komplett wie sie oben steht zu Beginn des Skripts eingefügt werden, oder alternativ über Dot-Sourcing referenziert werden, um Platz und Übersicht im Skript zu schaffen:
#Dot-Sourcing mit Angabe des Pfades zu der Datei, in der die Funktion hinterlegt ist
. “C:\Pfad\PS\parseCSR.ps1”
#Anschließend kann die Funktion wie gehabt verwendet werden
$result = ParseCSR “C:\Pfad\Zertifikatsantrag.csr” -Ausgabe
Good to know
Die Tatsache, dass alle Objekte als Array aufgeführt sind, erlaubt auch die Menge der enthaltenen Einträge zu prüfen.
Möchte man zum Beispiel prüfen, ob der CSR nur exakt einen Common Name enthält (bspw. weil ein System sonst nicht damit klar kommt) kann man dies wie folgt machen:
#CSR einlesen
$result = ParseCSR “C:\Pfad\Zertifikatsantrag.csr”
if($result.Subject.CommonName.Count -gt 1){
Write-Host “CSR enthält mehr als einen Common Name und entspricht nicht den Vorgaben”
}elseif($result.Subject.CommonName.Count -eq 1){
Write-Host “CSR enthält exakt einen Common Name und entspricht den Vorgaben”
}