Private/Wissen/C_Advance/C08_ScripteSignieren.ps1
<#
# Scriptdateien signieren Aufbau einer PKI, erzeugen von Zertifikaten und Skripte signieren und testen - **Hashtags** Zertifikat CodeSigningCert ExecutionPolicy - **Version** 2020.08.27 #> # ! WARUM SIGNIEREN ? # ! 1. Eine Signatur schützt ein Skript (ps1, psm1, .format.ps1xml, .type.ps1xml, ...) vor Manipulation! # ! 2. Der Signierer bestätigt mit seiner Signatur den endgültigen Status des Skriptes! # ! 3. Dritte können erkennen wer dieses Skripte signiert / erstellt hat. # ! 4. Die PowerShell kann über die Ausführungsrichtlinien so konfiguriert werden (AllSigned) dass nur Skripte ausgeführt werden, wenn folgende Voraussetzungen erfüllt sind: # ! (A) Die Skriptdatei wurde mit einem X.509-Zertifikat signiert! # ! (B) Der Zeitpunkt des Signier-Vorgangs in einem gültigen Bereich lag. # ! (C) Die signierte Datei wurde anschließend nicht manipuliert! # ! (D) Das Signierer-Zertifikat stammt von einer "Vertrauenswürdigen Stammzertifizierungsstelle" (Root) ab ODER es ist selbst in Root abgelegt worden! # ! (E) Signierer-Zertifikat wurde zusätzlich im Zertifikatspeicher für "Vertrauenswürdige Herausgeber" (TrustPublisher) abgelegt! #region Einfache Umsetzung über ein selbstsigniertes Zertifikat # ! 1. Testumgebung vorbereiten: Set-ExecutionPolicy -ExecutionPolicy 'AllSigned' -Scope 'Process' -Force Set-ExecutionPolicy -ExecutionPolicy 'AllSigned' -Scope 'CurrentUser' -Force Set-ExecutionPolicy -ExecutionPolicy 'AllSigned' -Scope 'LocalMachine' -Force Get-ExecutionPolicy -List New-Item -Path C:\ -Name Temp -ItemType Directory -Force | Select-Object -ExpandProperty FullName | Set-Location CertLM.msc # Zertifikate - Lokaler Computer CertMgr.msc # Zertifikate - Aktueller Benutzer # ! 2. Problem erkennen, s. Fehlermeldung: (LÖSUNG => Sginieren !) "' > > > Hallo Welt! < < < ' | Write-Host -ForegroundColor Red -BackgroundColor Yellow" | Set-Content -Path '.\Test.ps1' -Force . '.\Test.ps1' # ! 3. Mögliche Zertifikate die zum Signieren geeignet wären ermitteln, um evtl. das Erstellen eines eigenen Signierer-Zertifikates zu überspringen: Get-ChildItem -Path 'Cert:\CurrentUser\My' -CodeSigningCert # ! 4. Mein Signierer-Zertifikate erstellen: $params = @{ Subject = 'CN=_FirstName_LastName (PS Developer), E=v.nachname@abc.local' HashAlgorithm = 'SHA512' KeyExportPolicy = [Microsoft.CertificateServices.Commands.KeyExportPolicy]::ExportableEncrypted # ! GEFAHR ! CertStoreLocation = 'Cert:\CurrentUser\My' Type = [Microsoft.CertificateServices.Commands.CertificateType]::CodeSigningCert NotAfter = (Get-Date).AddYears(5) } $myPfxCert = New-SelfSignedCertificate @params # ! 5. Signierer-Zertifikate mit dem privaten Schlüssel exportieren $myPfxCertPassword = Read-Host -Prompt 'Das Passwort zum schützen des privaten Schlüssels angeben' -AsSecureString $myPfxCert | Export-PfxCertificate -Password $myPfxCertPassword -FilePath '.\MyCodeSigningCert.pfx' # ! 6. Das eben erstellte Signierer-Zertifikate rückstandlos aus allen Zertifikatsspeichern wieder löschen: Get-ChildItem -Path 'Cert:\' -Recurse | Where-Object -FilterScript { $_ -is [System.Security.Cryptography.X509Certificates.X509Certificate2] -and $_.Thumbprint -CEQ $myPfxCert.Thumbprint } | Remove-Item # ! 7. Aus dem Signierer-Zertifikate ein öffentliches Zertifikat erstellen: Get-PfxCertificate -FilePath '.\MyCodeSigningCert.pfx' | Export-Certificate -FilePath '.\PublicSignerCertificate.cer' -Type 'CERT' -Force # ! 8. Vertrauensstellung einrichten: # ! Das öffentliche Zertifikat ist z.Zt. nicht vertrauenswürdig, daher muss dies ... # ! ... 1. im Zertifikatsspeicher Root abgelegt werden, um dem Zertifikat generell zu vertrauen und ... # ! ... 2. im Zertifikatsspeicher TrustedPublisher abgelegt werden, um den signierten .PS1-Dateien im besonderen zu vertrauen. # ! Beide Schritte können auch per GPO oder Remote erfolgen. # ? 8.1. Zertifikat im Root-Store (Vertrauenswürdigen Stammzertifizierungsstelle) speichern Import-Certificate -FilePath '.\PublicSignerCertificate.cer' -CertStoreLocation 'Cert:\LocalMachine\Root' # ? 8.2. Zertifikat im TrustedPublisher-Store (Vertrauenswürdige Herausgeber) speichern Import-Certificate -FilePath '.\PublicSignerCertificate.cer' -CertStoreLocation 'Cert:\LocalMachine\TrustedPublisher' # ! 9. Aktueller Stand der Dinge: Get-ChildItem -Path .\Test.ps1, ` # ? Die zu signierende .PS1-Datei .\MyCodeSigningCert.pfx, ` # ? Das Signierer-Zertifikate mit dem geschützten privaten Schlüssel .\PublicSignerCertificate.cer # ? Öffentliches Signierer-Zertifikate zum bestätigen von Signaturen $PublicSignerCertificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new("$(Get-Location)\PublicSignerCertificate.cer") # ? Das Öffentliches Signierer-Zertifikate liegt im Zertifikatsspeicher Root, um dem Zertifikat generell zu vertrauen. Get-ChildItem -Path 'Cert:\*\Root' -Recurse | Where-Object Thumbprint -CEQ $PublicSignerCertificate.Thumbprint # ? Das Öffentliches Signierer-Zertifikate liegt im Zertifikatsspeicher TrustedPublisher, um den signierten .PS1-Dateien im besonderen zu vertrauen. Get-ChildItem -Path 'Cert:\*\TrustedPublisher' -Recurse | Where-Object Thumbprint -CEQ $PublicSignerCertificate.Thumbprint # ! 10. Eichrichtung abgeschlossen, es folgt nun das Signieren: $cert = Get-PfxCertificate -FilePath 'C:\temp\MyCodeSigningCert.pfx' Set-AuthenticodeSignature -FilePath 'C:\temp\test.ps1' -Certificate $cert -HashAlgorithm SHA512 -TimestampServer http://timestamp.digicert.com # ! <= OPTIONAL um die Gültigkeitsdauer zu verifizieren # ! 11. Signieren abgeschlossen, es folgt nun das Testen: # ? Wird .PS1-Datei nun problemlos ausgeführt? . '.\Test.ps1' # ? Für Testzwecke einmal den Dateiinhalt manipulieren: Start-Process -FilePath '.\Test.ps1' # ? Wird .PS1-Datei nun NICHT mehr ausgeführt? . '.\Test.ps1' # ? Die digitale Signatur sollte UNGÜLTIG sein? Get-AuthenticodeSignature -FilePath '.\Test.ps1' | Format-List -Property * # ? Die Manipulation der der .PS1-Datei wieder rückgängig machen: Start-Process -FilePath '.\Test.ps1' # ? Wird .PS1-Datei nun problemlos ausgeführt? . '.\Test.ps1' # ? Die digitale Signatur sollte GÜLTIG sein? Get-AuthenticodeSignature -FilePath '.\Test.ps1' | Format-List -Property * # ! 12. Die Tests sind abgeschlossen, es folgt nun das Aufräumen: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine -Force Get-ExecutionPolicy -List # TIPP: Get-ChildItem ..\AKPT\ -Force -Recurse | Unblock-File $PublicSignerCertificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new("$(Get-Location)\PublicSignerCertificate.cer") Get-ChildItem -Path Cert:\LocalMachine\Root, Cert:\LocalMachine\TrustedPublisher | Where-Object Thumbprint -CEQ $PublicSignerCertificate.Thumbprint | Remove-Item Get-ChildItem -Path .\Test.ps1, .\MyCodeSigningCert.pfx, .\PublicSignerCertificate.cer | Remove-Item #endregion |