Powershell S/MIME Certificat Exporter

Ich habe ein Skript benötigt, welches S/MIME Verschlüsselungs-Zertifikate exportiert um diese für eine Client-Migration zu erhalten. Das Skript setzt voraus, dass der S/MIME Private Key exportiertbar ist. Wenn das nicht gegeben ist, dann kann das Zertifikat nur noch an der CA mittels Key Recovery Agent (falls konfiguriert) oder Mimikaz wiederhergestellt werden.

Es kann auch sein, dass die Suchroutine nach dem richtigen Zertifikat angepasst werden muss, je nachdem was man als Key Usage für das S/MIME-Zertifikat hinterlegt hat.

Um des Export möglichst einfach für Endbenutzer zu machen, habe ich hierzu auch eine GUI außenherum gebaut.

<#
.SYNOPSIS
    This script exports an S/MIME certificate with a private key and sends it as an email attachment.
.DESCRIPTION
    This script prompts the user to select an S/MIME certificate, exports it with the private key to a PFX file,
    and then sends an email with the exported PFX file as an attachment. The user is prompted to enter a password
    for the private key. After sending the email, the script deletes the PFX file.
.NOTES
    File Name: Export-SMIMECertificate.ps1
    Author   : Ralf Entner
    Date     : 2024-03-24

    Version History:
    1.0 - [2024-03-24] - Initial version.
    1.1 - [2024-04-01] - Only certificate for Encryption and language specific settings 

    Prerequisites:
    - This script requires Windows PowerShell.
    - The user running the script must have permission to access the S/MIME certificate.
    - The script relies on the user having access to Outlook for sending emails.
    - The user must enter a password for certificate export
#>

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Create a new instance of Windows Form
$form = New-Object System.Windows.Forms.Form
$form.Text = "S/MIME Certificate Exporter"
$form.Size = New-Object System.Drawing.Size(500,300)
$form.StartPosition = "CenterScreen"

# Create a label for certificate selection
$labelCert = New-Object System.Windows.Forms.Label
$labelCert.Location = New-Object System.Drawing.Point(10,20)
$labelCert.Size = New-Object System.Drawing.Size(460,20)
$labelCert.Text = "Select the S/MIME certificate to export:"
$form.Controls.Add($labelCert)

# Create a dropdown list (ComboBox) for certificates
$comboBoxCert = New-Object System.Windows.Forms.ComboBox
$comboBoxCert.Location = New-Object System.Drawing.Point(10,40)
$comboBoxCert.Size = New-Object System.Drawing.Size(460,20)
$form.Controls.Add($comboBoxCert)

# Create a label for password
$labelPass = New-Object System.Windows.Forms.Label
$labelPass.Location = New-Object System.Drawing.Point(10,80)
$labelPass.Size = New-Object System.Drawing.Size(460,20)
$labelPass.Text = "Enter password for private key:"
$form.Controls.Add($labelPass)

# Create a textbox for password
$textBoxPass = New-Object System.Windows.Forms.TextBox
$textBoxPass.Location = New-Object System.Drawing.Point(10,100)
$textBoxPass.Size = New-Object System.Drawing.Size(460,20)
$textBoxPass.PasswordChar = "*"
$form.Controls.Add($textBoxPass)

# Get OS language for certificate oid friendly name

if((Get-WinSystemLocale).Name -eq "de-dE"){

    $OIDFriendlyName = 'Schlüsselverwendung'

} else{

    $OIDFriendlyName = 'KeyUsage'

}

# Get certificates for "secure email configuration"
$certificates = Get-ChildItem -Path cert:\CurrentUser\My | ?{$_.EnhancedKeyUsageList -like "*1.3.6.1.5.5.7.3.4*"}
# Get only certificates for de/encryption
$certificates = $certificates | Where-Object{ $_.Extensions | Where-Object{ ($_.Oid.FriendlyName -eq $OIDFriendlyName ) -and ($_.Format(0) -like "*(20)*") }}

# Populate the ComboBox with S/MIME certificate subjects
foreach ($cert in $certificates) {
    $comboBoxCert.Items.Add($cert.Thumbprint)
}

# Create an export button
$buttonExport = New-Object System.Windows.Forms.Button
$buttonExport.Location = New-Object System.Drawing.Point(150,140)
$buttonExport.Size = New-Object System.Drawing.Size(200,60)
$buttonExport.Text = "Send S/MIME Certificate via Outlook Mail"
$buttonExport.Add_Click({
    $selectedThumbprint = $comboBoxCert.SelectedItem
    if ([string]::IsNullOrWhiteSpace($selectedThumbprint)) {
        [System.Windows.Forms.MessageBox]::Show("Please select your S/MIME certificate.", "Error", 
        [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        return
    }

    # Get the selected certificate
    $cert = $certificates | Where-Object {$_.Thumbprint -eq $selectedThumbprint}

    # Export the S/MIME certificate with private key
    $exportPath = $env:TEMP + "\"+ $cert.Thumbprint + ".pfx"
    $password = $textBoxPass.Text

    if ([string]::IsNullOrWhiteSpace($password)) {
        [System.Windows.Forms.MessageBox]::Show("Please enter a password for the certificate.", "Error", 
        [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        return
    }

    try {
        Export-PfxCertificate -Cert $cert -FilePath $exportPath -Password (ConvertTo-SecureString -String $password -AsPlainText -Force)
        [System.Windows.Forms.MessageBox]::Show("S/MIME Certificate exported successfully.", "Success", 
        [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
    } catch {
        [System.Windows.Forms.MessageBox]::Show("Error exporting S/MIME certificate with private key: $_", "Error",
        [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        return
    }

    # Create a new Outlook application object
    $outlook = New-Object -ComObject Outlook.Application

    # Create a new mail item
    $mail = $outlook.CreateItem(0)

    # Set email properties
    $mail.Subject = "S/MIME Certificate"
    $mail.Body = "Please find the S/MIME certificate attached."

    # Attach the certificate file
    $attachment = $exportPath
    $mail.Attachments.Add($attachment)

    # Display the mail item
    $mail.Display()

    # Delete the PFX file after sending the email
    Remove-Item -Path $attachment -Force

})
$form.Controls.Add($buttonExport)

# Show the form
$form.ShowDialog() | Out-Null

RDS Terminalserver: Published App meldet beim Starten "Unbekannten Hersteller"

Problem:
Ich habe eine Published App für die Benutzer zur Verfügung gestellt und veröffentlich diese entweder über die RDWeb-Webseite des Terminalservers oder ich hänge die Apps direkt bei den Windows-Clients unter "Remote-App zugreifen" ein. Dabei wird beim Starten immer folgende Fehlermeldung angezeigt:


Lösung:
Zur Lösung müssen zwei Sachen umgesetzt werden:
1. Man muss das Zertifikate des RDS-Servers als "Vertrauenswürdige Stammzertifizierungsstelle" und als "Vertrauenswürdige Herausgeber" veröffentlichen (z.B. mittels GPO)
2.) Man muss den Thumbprint des veröffentliche Zertifikates als vertrauenswürdig einstufen (z.B. mittels GPO)

Also bauen Sie sich eine Zertifikats-GPO, soweit noch nicht vorhanden. Hierin importieren Sie das RDS-Zertifikat unter "Computerkonfiguration" -> "Windows-Einstellungen" -> "Sicherheitseinstellungen" -> "Richtlinien für öffentliche Schlüssel" unter den Punkten "Vertrauenswürdige Stammzertifizierungsstellen" und unter "Vertrauenswürdige Herausgeber".
Jetzt öffnen Sie das Zertifikat und kopieren sich den Thumprint unter den Eigenschaften heraus.
Diesen Thumprint fügen Sie dann in folgender GPO ein, um das Zertifikat vertrauenswürdig zu machen:
"Computerkonfiguration" -> "Administrative Vorlagen" -> "Windows-Komponenten" -> "Remotedesktopdienste" -> "Remotedesktopverbindungs-Client" ->"SHA1-Fingerabdruck von Zertifikaten angeben, die vertrauenswürdige RDP-Hersteller darstellen".

HINWEIS:
Der Thumbprint muss OHNE Leerzeichen eingegeben werden und mein 2016er-Server wollte unbedingt die Buchstaben als Großbuchstaben.

Zertifikate: Erstellen eines Self Signed Certificate mittels Powershell (z.B. RDS Published Apps)

Problem:
Ich habe eine Published App auf einem Server 2016 mit RDS. Für das Starten der App ohne Fehlermeldung benötige ich ein Zertifikat. Wenn ich das über die GUI erstelle, dann ist dieses immer nur 1 Jahr gültig. Das ist zwar sicherheitstechnisch korrekt, aber für eine Applikation für 10 Mitarbeiter jedes mal ein größerer Aufwand.

Lösung:
Ich erstelle auf dem RDS Server der Published App einfach ein Self Signed Certificate mittels Powershell mit entsprechenden Namen und angepasster Gültigkeitsdauer.

Hier der Befehl:
New-SelfSignedCertificate -Subject “server.domain.local” -DNSName “server.domain.local”, “rds.domain.local”, "Netbiosname Server" -CertStoreLocation “cert:\LocalMachine\My” -KeyAlgorithm RSA -KeyLength 2048 -KeyExportPolicy Exportable -NotAfter (Get-Date).AddYears(5)


Erklärungen:
DNSName: Hier geneben Sie alle FQDNs kommegetrennt an, über die der Server erriechbar sein soll. Nettes Feature...ich kann hier auch IP-Adressen und NetBIOS-Namen angeben, da ich nicht den Restriktionen einer öffentlichen Zertifikatsstelle unterliege.

-KeyExportPolicy: Das Zertifikat bzw. der private Schlüssel sollte exportierbar sein, falls man diesen sichern möchte.

-NotAfter: Hier kann man ein Gültigkeitsdatum des Zertifikats angeben. Ich selbst nehme immer den heutigen Tag (Get-Date) und Addiere x Jahre dazu (hier z.B. 5 Jahre).

Das Zertifikat verteile ich dann per GPO an alle Clients und setze noch den SHA1-Thumbprint als vertrauenswürdig um die unschöne Fehlermeldung zu unterdrücken.
Das habe ich hier erklärt: RDS Terminalserver: Published App meldet beim Starten "Unbekannten Hersteller"

Exchange: Kostenloses SAN-Zertifikat bei StartSSL für Serverumzug

Problem:
Während meines Exchange-Server-Swings habe ich temporär ein zweites SAN-Zertifikat benötigt und wollte ein öffentliches haben, da sonst die Smartphones über ActiveSync Probleme machen!

Lösung:
Ich habe bei StartSSL ein kostenloses SAN-Zertifikat bekommen. Der Vorteil für mich, gegenüber von Let'sEncrypt", das Zertifikat ist ein Jahr gültig (nicht nur 3 Monate) und ich konnte eine CSR-Anfrage von meinem Exchange "reinkippen".

Nachteil:
Das Zertifikat ist nur ein Jahr gültig und wird von Apple als unsicher eingestuft (Nicht verwunderlich). Der Internet Explorer hat das Zertifikat als valid eingestuft, da hier noch die Root-CA als vertrauenswürdig gilt.

Als temporäres OWA- und SMTP-SAN-Zertifikat hat es für mich gereicht und hat auch super geklappt!

Franky hat hier eine super tolle Anleitung dazu gemacht (Wie immer!!): Frankys Web: Kostenlose SAN-Zertifikate auch bei StartSSL

...und StartSSL findet Ihr hier: StartSSL.com

ActiveDirectroy-CA: Umzug auf neuen Server mit neuem Namen

Problem:
Ich musste meine ActiveDirectory-CA auf einen neuen Server mit neuem Namen umziehen.
Dafür gibt es einige Anleitungen im Netz und ich habe mir eine Anleitung aus diesen gebaut, mit der ich gut gefahren bin.

Lösung:
Grundannahme hier ist der Umzug einer AD-CA von einem Server auf einen neuen, wodurch sich der Computername ändert und der alte Server außer Betrieb geht!

--- AKTIONEN AUF DEM ALTEN SERVER ---

1.) WICHTIG: Vorher Sicherung des alten Servers erstellen!!!
2.) CA-Datenbank und Key sichern


WICHTIG: Export der Konfiguration in einen leeren Ordner
3.) Registry-Key der alten CA sichern: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration
4.) Templates exportieren mittels Befehl "certutil.exe –catemplates > templates.txt"
5.) CA deinstallieren oder zumindest deaktivieren


--- AKTIONEN AUF DEM NEUEN SERVER ---
1.) Backup-Ordner vom alten Server auf neuen kopieren (z.B. Desktop)
2.) Privaten Key (.P12-Datei im Backup-Ordner) importieren mittels Kennwort
3.) CA-Dienst als Rolle auf neuen Server installieren (Zertifikatsdienst + Webentrollment)
4.) Konfiguration der zuvor gesicherten CA wiederherstellen über CA-Konsole (Sieht aus wie Sicherung)
5.) Backup der Registry einspielen -WICHTIG: Hier müssen folgende Werte angepasst werden:
- CAServer-Eintrag auf neuen Namen ändern
- Eventuelle Pfadänderungen wie DBDirectory, DBTempDirectory anpassen
(Sollten Sie auf dem alten und neuen Server die Standard-Installation durchgeführt haben, dann ist keine Anpassung der Pfade nötig)
6.) Berechtigungen der CDP und AIA Container mittels ADSI-Editor auf neuen Server drehen
- Hierzu ADSI-Editor mit folgender Verbindung starten: CN=Public Key Services,CN=Services,CN=Configuration,DC=domain,DC=local
- Hier die Berechtigungen unter Sicherheit für die einzelnen Einträge unter CDP und AIA auf den neuen Server drehen.

Ich hatte noch einen Fehler in der pkiview.msc stehen, dass eine DeltaCRL-Location fehlerhaft ist. Das ist natürlich richtig, da die Location noch auf den alten Zertifikatsserver per http verweist. Man kann dann einfach eine neue komplette Sperrliste veröffentlichen und dann ist der Fehler auch weg! (Siehe Quellen-Link "How to Publish New Certificate Revocation List...")

FERTIG!

Quellen:
How to Publish New Certificate Revocation List (CRL) from Offline Root CA to Active Directory and Inetpub
Windows Root Zertifizierungsstelle migrieren/umziehen
Zertifizierungsstelle verschieben–neuer Servername
“Das einzig sichere System müsste ausgeschaltet, in einem versiegelten und von Stahlbeton ummantelten Raum und von bewaffneten Schutztruppen umstellt sein.”
Gene Spafford (Sicherheitsexperte)