1 Standards
1.1 Der X.509-Standard
Spricht man heute über Zertifikate und PKI, so meint man meist den Standard X.509v3. Viele Details dazu findet man beispielsweise auf der oben verlinkten Seite von Wikipedia.
Wichtig für das Verständnis sind die Ziele dieses von der Internationale Fernmeldeunion ITU-T im Jahre 1998 festgelegten Standards, die da wären
- technisch offen
- herstellerunabhängig
- plattformunabhängig
- erweiterbar
- Das X.509 Zertifikat besitzt einen öffentlichen und einen privaten Schlüssel. Solange der private Schlüssel nicht an Dritte weitergegeben wird, identifiziert dieser den rechtmäßigen Zertifikatsinhaber eindeutig.
Ich hatte in den letzten Jahren mit verschiedenen Herstellern und Anbietern von Sicherheitslösungen zu tun. Man möchte es nicht glauben, aber etliche dieser Hersteller - darunter auch große Namen -, versuchen Lösungen abseits dieses Standards zu verkaufen. Die Pro-Argumente lauten dann, daß man sich mit ihrer Lösung die Kosten einer PKI-Infrastruktur sparen kann. Die Nachteile bestehen allerdings in der Herstellerabhängigkeit, schwierigere Erweiterbarkeit der Lösung, Schwierigkeiten bei Plattformwechseln, etwa win2003 auf win2008. Schließlich verwendet man keinen anerkannten Sicherheitsstandard, auf den man sich bei Überprüfungen und Audits beziehen kann.
Beispiel 1: Bestimmen des Typs der Zertifikate im currentuser/ My - Store
$cert=@()
$Cert=(GCI cert:/currentuser/my)
$Cert | format-table subject,
@{
Label="Typ"
Expression={$_.getformat()}} -autosize
#Ausgabe
Subject Typ
------- ---
CN=Root Agency X509
CN=Dom1-DC1-RootCA, DC=Dom1, DC=intern X509
CN=Dom1-RootCA, DC=Dom1, DC=intern X509
CN=Dom1-CA01, DC=Dom1, DC=intern X509
- Die Formatierung der Tabelle ist im
- Die Behandlung des Zertifikatsproviders erfolgt im
Ein User oder Computer, der sich X.509-zertifikatsgestützt bei einem anderen Rechner authentisiert, legt dem Zielrechner sein Zertifikat vor. Der Zielrechner überpüft nun anhand verschiedener Kriterien, ob er diesem Zertifikat vertrauen kann.
Die Kriterien lauten
a) vertraue ich generell der Certification Authority (CA), die dieses Zertifikat ausgestellt hat (Zertifikatskette)
b) wurde dieses Zertifikat wirklich von dieser CA ausgestellt
c) Liegt das heutige Datum im Gültigkeitszeitraum (von ... bis ...)
d) ist der Absender wirklich derjenige, der er vorgibt zu sein (öffentlicher Schlüssel/ privater Schlüssel)
e) ist das Zertifikat gesperrt (Sperrliste)
Erst wenn all diese Punkte positiv geprüft wurden, vertraut der Zielrechner dem Zertifikat und damit seinem Besitzer und Absender. In den folgenden Kapiteln wird es unter anderem darum gehen, diese Fragen und Kriterien per Powershell zu überprüfen. Wiesooft erhält man als Nebeneffekt zum Skripten häufig tiefe Einblicke in das Thema, da man die Thematik erstmal gründlich verstanden haben muss, bevor ein vernünftiges Skript entwickelt werden kann.
Für mehr Hintergrundwissen besorgt euch bitte entsprechende Literatur wie das schon an anderer Stelle erwähnte Werk von Brian Komar "Windows Server 2008 - PKI and Certificate Security" aus dem MS-Press Verlag.
1.2 Public-Key Cryptography Standards (PKCS)
2 CertificateLocation/ CertificateStore
Zertifikate werden lokal in einem bestimmten Zertifikatsstore (=Storename) gespeichert. die Stores können sich an zwei Speicherorten (=Storelocation) befinden. Die beiden Locations sind "CurrentUser" und "LocalMachine", entsprechend ihrem physikalischen Speicherort in der Registry:
"HKEY_CURRENT_USER\Software\Microsoft\SystemCertificates\"
oder für die LocalMaschine
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SystemCertificates\"
Auf Zertifikate unter "CurrentUser" kann ein Anwender mit seinem Kontext zugreifen, auf Zertifikate unter "LocalMachine" greift das System mit dem Systemkonto zu.
In der MMC im SnapIn "Zertifikate" sind diese Stores entweder in den Storelocations "Zertifikate - Aktueller Benutzer" oder "Zertifikate (Lokaler Compute)" zu finden. Per PS-Skript können auch noch weitere Stores angelegt werden. (siehe Beispiel 3)
Beispiel 1: Aufzählung der StoreLocations
[system.enum]::getnames([System.Security.Cryptography.X509Certificates.StoreLocation])
$storelocation = [System.Security.Cryptography.X509Certificates.StoreLocation]
[system.enum]::getnames($storelocation)
#Ausgabe
CurrentUser
LocalMachine
Beispiel 2: Aufzählung der default Storenames (Windows 7 Ultimate)
[system.enum]::getnames([system.security.cryptography.x509certificates.storename])
Membername | Beschreibung |
AddressBook | Der X.509-Zertifikatsspeicher für andere Benutzer. |
AuthRoot | Der X.509-Zertifikatsspeicher für Zertifizierungsstellen von Drittanbietern. |
CertificateAuthority | Der X.509-Zertifikatsspeicher für Zwischenzertifizierungsstellen. |
Disallowed | Der X.509-Zertifikatsspeicher für widerrufene Zertifikate. |
My | Der X.509-Zertifikatsspeicher für persönliche Zertifikate. |
Root | Der X.509-Zertifikatsspeicher für vertrauenswürdige Stammzertifizierungsstellen. |
TrustedPeople | Der X.509-Zertifikatsspeicher für direkt vertrauenswürdige Personen und Ressourcen. |
TrustedPublisher | Der X.509-Zertifikatsspeicher für direkt vertrauenswürdige Herausgeber. |
Wichtig für das Verständnis ist, daß im My-Store Zertifikate mit privatem Schlüssel liegen. In den anderen Stores liegen in erster Linie signierte Zertifikate, die keinen privaten Schlüssel enthalten und für die Verifizierung der Zertifkatskette erforderlich sind.
Beispiel 3: Anzeige aller vorhandenen Stores
Get-Childitem -path cert:\CurrentUser -name
#alternativ mit QAD
#Get-QADLocalCertificateStore -StoreLocation CurrentUser | Format-Table -AutoSize
#Ausgabe #gekürzt
Name : SmartCardRoot
Name : UserDS
Name : AuthRoot
...
Beispiel 4a: Anlage eines zusätzlichen Zertifikatsstores für den "aktuellen Benutzer" über .Net
$storename = "TestUserStore01"
$storelocation = "Currentuser"
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store($storename,$storelocation)
$store.Open("ReadWrite")
Beispiel 4b: Anlage eines zusätzlichen Zertifikatsstores für den "lokalen Computer" über .Net
$storename = "TestMachineStore01"
$storelocation = "Localmachine"
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store($storename,$storelocation)
$store.Open("ReadWrite")
Tipp: Man kann im Zertifikatssnapin der MMC zwischen den Stores Zertifikate per rechter Maustaste oder Drag&Drop kopieren und verschieben, um beispielsweise zu Troubleshooten oder Zertifikate zu testen. Dazu sind solche zusätzlichen leeren Stores recht nützlich.
3 Zertifikate in Dateien
3.1 Dateiformate für Zertifikate
Eine sehr gute Erklärung zu den gängigen Fileformaten für Zertifikatsdateien bietet wieder die Technet unter
"Von den Import- und Exportvorgängen für Zertifikate werden vier Dateiformate unterstützt. Wählen Sie das Format aus, das Ihren speziellen Anforderungen entspricht.
- Privater Informationsaustausch (PKCS #12)
Das PFX-Format (Personal Information Exchange, privater Informationsaustausch), das auch als PKCS #12 bezeichnet wird, ermöglicht die sichere Speicherung von Zertifikaten, privaten Schlüsseln und allen Zertifikaten in einem Zertifizierungspfad.
Das PKCS #12-Format kann als einziges Dateiformat zum Exportieren eines Zertifikats und dessen privatem Schlüssel verwendet werden.
- Syntaxstandard kryptografischer Meldungen (PKCS #7)
Das PKCS #7-Format unterstützt die Speicherung von Zertifikaten und allen Zertifikaten im Zertifizierungspfad.
- DER-codiert-binär X.509
Das DER-Format (Distinguished Encoding Rules) unterstützt die Speicherung eines einzelnen Zertifikats. Der private Schlüssel oder der Zertifizierungspfad kann mit diesem Format nicht gespeichert werden.
- Base64-codiertes X.509
Das Base64-Format unterstützt die Speicherung eines einzelnen Zertifikats. Der private Schlüssel oder der Zertifizierungspfad kann mit diesem Format nicht gespeichert werden."
3.2 ByteArrays
Da beim Export- und Import von Zertifikaten viel mit ByteArrays gearbeitet wird, will ich hier etwas näher auf diesen Datentyp eingehen.
Sehr gut beschrieben sind ByteArrays in diesem Blog-Artikel:
Sans - Windows Security Blog:
In die Tiefe dieses Artikels werde ich hier bei Weitem nicht vorstoßen!
Manchmal muß man Dateien nicht Wort für Wort und Zeile für Zeile einlesen, sondern binär Byte für Byte. In meinem Fall benötige ich diese Funktionalität für den Umgang mit in Dateien gespeicherten Zertifikaten, wie im Kapitel
Aber auch für die genaue Analyse von Dateien kann ein byteweiser-Import hilfreich sein.
Beispiel 1: Drei Methoden eine Zertifikatsdatei in ein ByteArray einzulesen
$filepath="c:\mycerts\test.cer"
#Methode 1: get-content
[byte[]] $data = get-content -encoding byte -path $filepath
$data
#Methode 2: .Net-Klasse File
[byte[]] $data=[System.IO.File]::ReadAllBytes($filePath)
$data
#Methode 3: .Net-Klasse Filestream
$FileStream = New-Object System.IO.FileStream ($filePath,[System.IO.FileMode]::Open,[System.IO.FileAccess]::read)
[int]$size = [math]::truncate($filestream.Length)
[byte[]] $data = new-object byte[] $size
$filestream.Read($data, 0, $size)
$filestream.Close()
$data
#identische Ausgabe für alle Methoden
80
102
108
115
79
101
...
"[byte[]] $data" ist die gleichwertige Kurzform von "[system.byte[]] $data"
Zu Methode 1)
Das cmdlet "get-content" eignet sich für den Import kleinerer Dateien bis etwa 200 kb. Bei größeren Dateien sollte man aus Performancegründen auf die .Net Methoden ausweichen.
Zu Methode 2)
MSDN:
Zu Methode 3)
MSDN:
MSDN:
Beispiel 2: Ein Zertifikat aus dem My-Store in ein Bytearray exportieren und in einer Datei speichern
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","Currentuser")
$store.Open("ReadOnly")
$Cert=($store.certificates)[0]
# identisch zu: $cert = (dir cert:\currentuser\my)[0]
$type = [System.Security.Cryptography.X509Certificates.X509ContentType]::pfx
$pass = ConvertTo-SecureString "Hurra" -AsPlainText -Force
[system.byte[]] $data = $cert.export($type, $pass) #siehe Export-Methode unten
$data
[System.IO.File]::WriteAllBytes("c:\mycerts\file.pfx", $bytes)
#Ausgabe
48
130
6
170
2
...
- die Exportmethode der X509Certificate-Klasse exportiert ein Zertifikat in ein ByteArray siehe MSDN:
- die möglichen Werte für X509ContentType findet man hier: MSDN:
- die Methode WriteAllBytes der Klasse "System.IO.File" schreibt alle Bytes in eine Datei MSDN: