1. Zertifikate

2 Standards

2.1 Der X.509-Standard

Wikipedia: X.509

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, dass 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, et.
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/ Root - Store

Set-StrictMode -Version "2.0"
Clear-Host

$CertStore = "CurrentUser/Root"

$Certs = @()
$Certs = (GCI Cert:/$CertStore)

$Certs | Format-Table Thumbprint,
 @{
   Label = "Subject";
   Expression={ $($_.Subject).SubString(0,35)}
  },
 @{   
   Label = "Typ";
   Expression={$_.GetFormat()}
 } -AutoSize
#mögliche Ausgabe

Thumbprint                               Subject                             Typ
----------                               -------                             ---
E3F0F64B6DD75EF287C4F8864BC85E5E6C90E3C9 CN=avast! Mail Scanner Root, O=avas X509
CDD4EEAE6000AC7F40C3802C171E30148030C072 CN=Microsoft Root Certificate Autho X509
BE36A4562FB2EE05DBB3D32323ADF445084ED656 CN=Thawte Timestamping CA, OU=Thawt X509
A43489159A520F0D93D032CCAF37E7FE20A8B419 CN=Microsoft Root Authority, OU=Mic X509


- Die Behandlung des Zertifikatsproviders erfolgt im Kapitel Handling von Zertifikaten

- Hilfe beim Erstellen solcher Tabellen findet ihr hier:  3.1 Selbstdefinierte Spalten zu Format-Table hinzufügen

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

  • vertraue ich generell der Certification Authority (CA), die dieses Zertifikat ausgestellt hat (Zertifikatskette)
  • wurde dieses Zertifikat wirklich von dieser CA ausgestellt
  • liegt das heutige Datum im Gültigkeitszeitraum  (von ... bis ...)
  • ist der Absender wirklich derjenige, der er vorgibt zu sein (öffentlicher Schlüssel/ privater Schlüssel)
  • 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.
 

2.2 Public-Key Cryptography Standards (PKCS)

Wikipedia: PKCS
 

In diesem Wikipediaartikel seht ihr die verschiedenen PKCS-Standards.
 

2.2.1 PKCS#9 Objekte und Attribute eines Zertifikats


Der Aufbau und die Elemente eines X509-Zertifikats findet ihr genau im PKCS#9 Standard beziehungsweise im RFC 2985 beschrieben.



 

2.2.2 Zertifikats-Formate: PKCS#7, PKCS#9, DER, Base64


Eine gute Erklärung zu den gängigen Fileformaten für Zertifikatsdateien bietet wieder die Technet unter Technet: Zertifikatdateiformate


"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 Showing Certificates/ 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 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.
 

3.1 Anzeige mit dem Zertifikats-SnapIn



Man sieht hier alle Zertifikate, die als Root einer Zertifikatskette akzeptiert werden. Zu beachten ist, dass die Anzahl dieser TrustedRoot-Zertifikate nicht 150 übersteigen sollte, da es sonst zu Problemen kommen kann.
https://support.microsoft.com/en-us/kb/2464556 
Ab Server 2012 ist das Problem behoben, da hier der RegistryEintrag "SendTrustedIssuerList" schon default auf 0 gesetzt ist.

In jedem Fall ist es interessant, sich die "physical stores" anzusehen, an denen man erkennen kann, auf welchem Weg das Zertifikat ein Trusted Root Zertifikat wurde:

 

Das Aktivieren der  Anzeige der physikalischen Stores in der GUI ist allerdings etwas versteckt:

1. Mit der Maus auf "Certificates (Local Computer) 

2. Unter Options -> View Options

3. Physical certificate stores 

http://blogs.msdn.com/b/muaddib/archive/2013/10/18/understanding-certificate-stores-and-publishing-certificates-for-smart-card-logon.aspx

 

3.2 Zertifikatsanzeige mit dem IE11

In Windows könnt ihr euch Zertifikate auch mit einem Webbrowser -wie hier gezeigt- dem "IE11" ansehen:


und Doppelklick auf das Zertifikat

 

3.3 Anzeige in der Registry (BLOB)


In der Registry sind die Zertifikate unter ihren Thumbprints gespeichert. Die weiteren Informationen liegen in einem (soweit ich weiß) unleserlichen "BLOB", was soviel wie "Binary Large Object" bedeutet


 


4 Stores und StoreLocations mit Powershell erforschen

Beispiel 1: Aufzählung der StoreLocations

Clear-Host
Set-StrictMode -Version "2.0"
 
#[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 

Clear-Host
Set-StrictMode -Version "2.0"
 
[System.Enum]::GetNames([System.Security.Cryptography.x509Certificates.Storename])
#mögliche Ausgabe auf Windows 8.1

AddressBook
AuthRoot
CertificateAuthority
Disallowed
My
Root
TrustedPeople
TrustedPublisher


dieselbe Information erhält man auch mit: certutil.exe -enumstore


Diese Liste findet sich auch in der MSDN wieder:
MSDN: Store Name Enumeration

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 3a: Anzeige aller vorhandenen Stores

Clear-Host
Set-StrictMode -Version "2.0"


Get-Childitem -path cert:\CurrentUser -name 
""
Get-Childitem -path cert:\LocalMachine -name 
#Ausgabe

SmartCardRoot
UserDS
AuthRoot


Beispiel 3b: Anzeige aller vorhandenen Stores/ Anzahl der enthaltenen Zertifikate (UserStore)

Clear-Host
Set-StrictMode -Version "2.0"

$Results =@{}
$Containers = Get-Childitem -path cert:\CurrentUser -Name
Foreach ($Container in $Containers){
  $CertsCount = (Get-Childitem -path cert:\CurrentUser\$Container -recurse |Measure-Object).Count
  $Results.Add($Container,$CertsCount)
  }

$Results.GetEnumerator() | Sort-Object Value -Descending | Select -First 5
#mögliche Ausgabe

Name                           Value  
----                           -----
Root                           31
AuthRoot                       21
CA                             8
TestUserStore01                1
Trust                          0     

 

Behaltet die Values dahingehend im Auge, dass ab ewa 100 Zertifikaten pro Container Probleme auftreten können:
Active Directory Team Blog: Certificate Trust List Size Problem Check (PKI)


Beispiel 3c: Vergleich aller vorhandenen Stores/ Anzahl der enthaltenen Zertifikate zwischen CurrentUser und Localmachine

Clear-Host
Set-StrictMode -Version "2.0"
 
Function Main{
  $CU_Results =@{}
  $LM_Results = @{}
 
  #reading all stores
  $CU_Containers = Get-Childitem -path cert:\CurrentUser -Name
  $LM_Containers = Get-Childitem -path cert:\LocalMachine -Name
 
  #collecting a list of all stores in storelocation "CurrentUser"
  Foreach ($CU_Container in $CU_Containers){
     $CertsCount = (Get-Childitem -path cert:\CurrentUser\$CU_Container -recurse |Measure-Object).Count
     $CU_Results.Add($CU_Container,$CertsCount)
   }
 
  #collecting a list of all stores in storelocation "LocalMachine"
  Foreach ($LM_Container in $LM_Containers){
     $CertsCount = (Get-Childitem -path cert:\LocalMachine\$LM_Container -recurse |Measure-Object).Count
     $LM_Results.Add($LM_Container,$CertsCount)
   }
 
  #Creating a list of all stores from both storelocations. Removing duplicates "-Unique"   
  $AllStores = $CU_Results.Keys + $LM_Results.Keys | sort -Unique  
 
  #creating a psobject "Storename, CertCount_CurrentUser, CertCount_LocalMachine 
  $PSStoreCollection =@()
  ForEach($Store in $AllStores){
    $PSStore = New-Object -TypeName Psobject -Property @{
    "StoreName" = $Store;
    "CertCount_CurrentUser" = $CU_Results.Get_Item($Store);
    "CertCount_LocalMachine" = $LM_Results.Get_Item($Store)
    } #PSStores
   $PSStoreCollection += $PSStore
  }#For
  
  #creating table 
  $PSStoreCollection = $PSStoreCollection | sort CertCount_LocalMachine -Descending
  $PSStoreCollection | Ft StoreName, `
                          @{Label = "Certs in MachineContainer";Expression={"{0}" -f ($_.CertCount_LocalMachine)}}, 
                          @{Label = "Certs in UserContainer";Expression={"{0}" -f ($_.CertCount_CurrentUser)}} -Auto
}#end Function Main
 
Main
#möglich Ausgabe auf Windows 8

StoreName                      Certs in MachineContainer Certs in UserContainer
---------                      ------------------------- ----------------------
Root                           37                        37                    
AuthRoot                       27                        27                    
CA                             4                         7                     
Windows Live ID Token Issuer   2                                               
avast! Mail Scanner Cache      1                                               
TrustedPublisher               1                         1                     
Homegroup Machine Certificates 1                                               
TrustedPeople                  0                         0                     
TrustedDevices                 0                                               
Trust                          0                         0                     
SmartCardRoot                  0                         0                     
My                             0                         0                     
Disallowed                     0                         0                     
ClientAuthIssuer               0                         1                     
avast! Mail Scanner Trusted    0                                               
Remote Desktop                 0                                               
UserDS                                                   0                     
ADDRESSBOOK                                              0      


Beispiel 4: Anlage eines zusätzlichen Zertifikatsstores für den "aktuellen Benutzer" über .Net

Clear-Host
Set-StrictMode -Version "2.0"

$StoreName = "Test01"
$StoreLocation = "Currentuser"  #for "LocalMachine" run as Powershell as Administrator
 
$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.

Wenn ihr wollt, könnt ihr im Snap-In mal jeweils im CurrentUser- und Localmachine ein Zertifikat aus dem Rootstore in den eben mit Beispiel 4 erstellten Store "Test01" verschieben und die Container jeweils mit dem Script aus Beispiel 3c vergleichen. Legt bzw. verschiebt ihr ein Zertifikat in der LocalMachine-Location, so ist das Zertifikat auch automatisch im CurrentUser-Store angelegt, umgekehrt nicht. Fehlende Zertifikate im Root- bzw. AuthRoot-Store lädt Windows übrigens automatisch wieder nach, sofern in der GPO kein anderer Mechanismus konfiguriert wurde. (Root Update Mechanismus)
 

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: PowerShell Byte Array And Hex Functions
In die Tiefen 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  Zertifikate - Handling von Zertifikaten an Beispiel 1a: "Exportieren eines Zertifikats in eine pfx-Datei (Klasse X509ContentType)" gezeigt wird.

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 (gekürzt)
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)


Zu Methode 3)


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
...

File.WriteAllBytes-Methode

 

Beispiel 3: Export aller Zertifikate aus dem NTAuthStore

Clear-Host
Set-StrictMode -Version "2.0"
 
Function Main{
 
  $LogPath = "C:\temp\Ntauth.log"
  $CSVPath = "C:\temp\Ntauth.csv"
 
  $Path = "CN=NTAuthCertificates,CN=Public Key Services,CN=Services,CN=Configuration,DC=Dom1,DC=net"
  $NTauth = [ADSI]"LDAP://$Path"
  $NTauthCertificates = $NTauth.cACertificate
  Read-CertProperties $NTauthCertificates
}
 
Function Read-CertProperties{
  Param($NTauthCertificates)
 
  $Certificates = @()
  ForEach ($NTauthCertificate in $NTauthCertificates){
   [psobject]$Certificate = `
    "" | Select-Object Subject,Issuer,Version,NotBefore,NotAfter,Thumbprint,SerialNumber,FriendlyName
 
 
     [Byte[]] $RawData = $NTauthCertificate
     $X509 = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2  
     $X509.Import($RawData)
     $Certificate.Subject = $x509.Subject
     $Certificate.Issuer  = $x509.Issuer
     $Certificate.Version = $x509.Version
     $Certificate.NotBefore = $x509.NotBefore
     $Certificate.NotAfter = $x509.NotAfter
     $Certificate.Thumbprint = $x509.Thumbprint
     $Certificate.SerialNumber = $x509.SerialNumber
     $Certificate.FriendlyName = $($($x509.PublicKey).Oid).FriendlyName
 
     $Certificates += $Certificate
     }# foreach
  
  $Certificates | Sort-Object NotAfter
  $Certificates | Out-File -FilePath $LogPath
  $Certificates | Export-Csv -Path $CSVPath -NoTypeInformation -Delimiter "~"
} #End function
 
 
Main