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

Outlook: "Externer Inhalt ist in sicheren Mails nicht zulässig" beim Weiterleiten von Mails

Bei Outlook erhält man beim Weiterleiten einer Mail den folgenden Hinweis angezeigt und alle externen Bilder werden nicht angezeigt:
Externer Inhalt ist in sicheren Mails nicht zulässig

Wenn man sich die Mail genauer anschaut, dann ist diese digital signiert (Bei uns war es eine DHL-Mail). Nach etwas Recherche hat sich herausgestellt, dass es kein Bug ist sondern eine Sicherheitsfunktion von Outlook 2010 - 2016. Diese Funktion wurde per Update im November 2018 eingeführt und aktiv genommen. Hintergrund ist die Ausnutzung der Efail-Schwachstelle.
Genaueres könnt Ihr hier nachlesen: Outlook: "Externer Inhalt ist in sicheren Mails nicht zulässig" - BornCity
Leider gibt es aktuell keine GPO-Vorlage um das abzuschalten. Ich habe diese jetzt in einer GPO per Registrykey behoben. Hierzu muss man folgenden Regkey setzen:
Path: HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\Office\xx.0\Outlook\Security
Valuename: DisallowSMIMEExternalContent
Valuetyp: REG_DWord
Value: 0
Office-Versionsnummern xx: 14 = Office 2010, 15 = Office 2013 und 16 = Office 2016
Quelle: Outlook for Windows blocking external content by default

Outlook: Alle Kontakte auf ein mal als "gelesen" makieren

Problem:
Ich habe bei einem Client entweder neue Kontakte importiert oder aber Kontaktordner aus öffentlichen Ordnern eingehängt. Dabei markiert Outlook alle Kontakte "fett" als nicht gelesen. Es ist nun sehr mühsam alle Kontakte zu öffnen um die Makierung zu entfernen.

Lösung:
Gott sei Dank gibt es Schnell Tasten!! Folgende Abfolge markiertalle Kontakte als gelesen:

- Klicken Sie auf Konakte und in den betroffenen Ordner
- Hier wählen Sie mittels STRG+A alle Kontakte an
- Nun drücken Sie STRG+Q um alle Kontakt als gelesen zu makieren
- FERTIG!

Outlook Suche: Onlineergebnisse sind auf 250 Elemente begrenzt! Es werden nicht alle Suchergebnisse angezeigt

Ich hatte folgenden Fehler bei Outlook-User, die eine größere Ordnerstruktur durchsucht haben


Die User bekommen tatsächlich nur 250 Suchergebnisse angezeigt, was jetzt nicht besonders viel ist, wenn man eine ältere Mail sucht!

Ich dachte erste, dass es was mit den MaxObjects zu tun hat und habe daher die MaxObjects für Folder, FolderView, Message und MessageView auf 2000 gesetzt. Heute besteht das Problem weiterhin. Nach etwas Recherche habe ich doch dann tatsächlich einen Eintrag gefunden, der das Problem schildert und löst.
Dieses Einstellung kann erst seit Exchange 2013 CU11 geändert werden und muss aber manuell angepasst werden.

1.) Öffnen Sie die Datei "%ExchangeInstallPath%/Bin/Microsoft.Exchange.Store.Worker.exe.config"
2.) Suchen Sie hier den Schlüssel /runtime
3.) Fügen Sie danach folgende Zeilen hinzu:

<appSettings>
      <add key="MaxHitsForFullTextIndexSearches" value="1000" />
</appSettings>

Hiermit werden die Suchergbnisse auf 1.000 Elemente erweitert.
4.) Starten Sie den Informationsspeicher durch

Damit aber noch nicht genug, da wir hiermit die Beschränkung auf dem Server aufgehoben haben, aber NICHT am Client.
Hier gibt es noch eine Einstellung im Outlook, die die Suchergebnisse ebenfalls beschränkt:

Scheinbar gibt es aber in den Microsoft-GPOs keine Einstellungsmöglichkeit und somit muss man einen RegKey per GPO ausstreuen. Diese Einstellung gibt es aber erst ab Outlook 2013. Hier die RegKeys dazu:
Office 2013: HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook\Search
Office 2016: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Search
REG_DWORD “SearchResultsCap” anlegen
Hier den Wert “0” vergeben (Hier kann auch der gleiche Wert wie auf dem Server eingetragen werden)

Ein scheinbar kleines Problem hat mich gut beschäftigt!

Quelle: somoit: Exchange 2013 – Solved the “Search results limited to 250” bug

Exchange Autodiscover: Wie wird die Autodiscover-URL ermittelt?

Problem:
Für mich hat sich die Frage gestellt, wie der Outlook-Client (gerade außerhalb der Organisation über Outlook Anywhere) den Autodiscover FQDN bekommt, die er anfragt um seine Autodiscover-Informationen zu bekommen.

Lösung:
Der Outlook-Client geht hier nach einem fest definierten Schema vor, dass man wissen sollte, da man sonst nicht versteht, warum der Client keine Autodiscover-Informationen erhält.

WICHTIG VORWEG: Als Mail-Domäne nimmt der Client nicht die Windows-Domäne sondern die Mail-Domäne der PRIMÄREN Mail-Adresse des Benutzers
Das kann Probleme machen, wenn man einem Benutzer nur eine interne Mailadresse gibt oder dieser verschiedene externe Mail-Adresse zugewiesen bekommen hat. Vor allem, wenn der Benutzer per Outlook Anywhere zugreift und keinen SCP erreichen kann und somit die Autodiscover-URLS abklappert.

Folgenden Ablauf durchläuft der Client für das Autodiscover-Ermittlung:
1.) Abfrage SCP (Service Connection Point) aus dem AD (ClientAccessServer AutodiscoverServiceInternalURI)
2.) Anfrage auf https://mail-domäne/autodiscover/autodiscover.xml
3.) Anfrage auf https://autodiscover.mail-domäne/autodiscover/autodiscover.xml
4.) Anfrage auf http://autodiscover.mail-domäne/autodiscover/autodiscover.xml
5.) Abfrage eines SRV-Records
6.) Verwendung der lokalen autodiscover.xml, soweit bereits vorhanden

Quelle: SebastianHetzel.net: Exchange Autodiscover

Exchange 2007 - 2013 Migration: Outlook verlangt ständig Anmeldedaten (Credential Popup) für Öffentliche Ordner

Problem:
Ich bin gerade bei einer Migration von Exchange 2007 auf Exchange 2013.
Da die Migration im laufenden Betrieb durchgeführt wird und Exchange 2013 nicht direkt auf die Öffentlichen Ordner von Exchange 2007 zugreifen kann, musste ich die Discovery-Mailbox-Variante wählen um den Zugriff der umgezogenen Clients zu ermöglichen (siehe Quelle unten).
Dabei ist aufgefallen, dass alle umgezogenen Benutzer auf Exchange 2013 mehrmals am Tag die Verbindung zum Öffentlichen Ordner auf Exchange 2007 verloren haben und sich ein Anmeldefenster dazu öffnet. Das war sehr lästig und ich habe viel an den IIS-Einstellungen vom alten und neuen Exchange versucht - alles erfolglos!
Daraufhin habe ich herausgefunden, dass ich Outlook über die Exchange-Proxy-Einstellungen zu einem TCP-Connect "zwingen" kann, indem ich dort den Haken "Bei schneller Verbindung zuerst über HTTP verbinden" entferne. Damit versucht Outlook erst eine TCP-Verbindung und dann eine HTTP-Verbindung. Das klappt super gut und die Verbindung zum alten Server geht über RPC/TCP und zum neuen über RPC/HTTP.
Das nächste Problem: Das Autodiscover überschreibt meine Einstellungen und setzt mir den Haken immer wieder neu!!!

Lösung:
Man kann mittels Registry eine Policy setzen, wodurch man die Einstellung dauerhaft festsetzen kann.
Hier die Policy, wie ich Sie aktuell für Outlook 2007 - 2013 erfolgreich einsetze:

Key : HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\office\xx.0\Outlook\RPC
Value Name : proxyserverflags
Value Type : REG_DWORD
Value : 39 (27h)


Wobei xx für die jeweilige Office-Version steht (12 = Office 2007, 14 = Office 2010, 15 = Office 2013, 16 = Office 2016)

Das ist zwar nicht die sauberste Lösung, da ich eigentlich das Problem am Exchange 2007 nicht gelöst habe doch will ich gerade nicht die Konfiguration des alten Servers so extrem abändern, dass mir etwas anderes "um die Ohren fliegt". Außerdem wird der alte Exchange-Server nach der Migration sowieso abgeschaltet und damit erledigt sich dann auch das Problem.

Quelle für Flags: getadmx.com: RPC/HTTP-Verbindungs-Flags

Quelle Discover Public Folder 2013: Technet: Konfigurieren älterer öffentlicher Ordner, wenn sich die Postfächer der Benutzer auf Exchange 2013-Servern befinden


“Das einzig sichere System müsste ausgeschaltet, in einem versiegelten und von Stahlbeton ummantelten Raum und von bewaffneten Schutztruppen umstellt sein.”
Gene Spafford (Sicherheitsexperte)