Zum Inhalt springen
ecdsa.com
← Alle Artikel

Authenticode-Codesignatur: Schlüssel, Zeitstempel, Prüfung

Wir haben eine kleine ausführbare Windows-Datei mit osslsigncode 2.14 und einem Zeitstempel von DigiCert signiert und sie dann drei Tests unterzogen: eine ohne Zeitstempel signierte Kopie, ein geändertes Byte im Code und versteckte Daten in der Zertifikatstabelle. Die Regeln stammen aus den Code Signing Baseline Requirements 3.11.0 des CA/Browser Forums und aus Microsofts eigener Dokumentation, geprüft im Oktober 2026.

Kurz gesagt. Authenticode bettet eine Struktur SignedData nach PKCS #7 in eine Tabelle am Ende einer Windows-PE-Datei ein; die Signatur deckt einen Hash der Datei ab, der die Prüfsumme, den Verweis auf diese Tabelle und die Tabelle selbst auslässt. Seit dem 1. Juni 2023 muss ein öffentlich vertrauenswürdiger Codesignaturschlüssel in einem Hardwaremodul erzeugt und verwendet werden (einem Token, einem HSM im eigenen Rechenzentrum oder einem Cloud-HSM-Dienst mit vollständiger Audit-Protokollierung); mit der gewohnten .pfx-Datei auf einem Build-Server bekommst du also kein Zertifikat mehr. Zertifikate, die seit dem 1. März 2026 ausgestellt werden, gelten höchstens 460 Tage, und Microsofts Artifact Signing stellt Zertifikate aus, die 72 Stunden gelten. Damit ist der Zeitstempel nach RFC 3161 das, was eine Signatur gültig hält: Signiere immer mit /fd SHA256 /tr <TSA URL> /td SHA256. EV-Zertifikate verschaffen keine sofortige SmartScreen-Reputation mehr. Prüfe unter Windows mit signtool verify /pa /tw, auf anderen Systemen mit osslsigncode verify.

Was signiert wird

Eine PE-Datei (.exe, .dll, .sys) hat in ihrem optionalen Header ein Datenverzeichnis. Eintrag 4, die Zertifikatstabelle (Certificate Table), enthält Datei-Offset und Größe einer Liste von WIN_CERTIFICATE-Strukturen, die an das Ende der Datei angehängt ist. Bei Authenticode hat der Eintrag die Revision 0x0200 und den Typ WIN_CERT_TYPE_PKCS_SIGNED_DATA (0x0002) und enthält eine Struktur SignedData nach PKCS #7 (PE-Format).

Signiert wird nicht die Datei selbst, sondern ein SpcIndirectDataContent, das den Hash der Datei enthält. Dieser Hash kann die Signatur selbst nicht abdecken. Deshalb muss er, mit Microsofts Worten, „bestimmte Felder im PE-Image ausschließen, etwa die Prüfsumme und den Eintrag der Zertifikatstabelle in den Datenverzeichnissen des optionalen Headers“ – und dazu die Zertifikatstabelle selbst. Alles andere fließt in den Hash ein: Header, Sektionen und Daten nach der letzten Sektion.

Unser Beispiel ist ein 1,6 MB großes Programm, mit Go 1.27.3 cross-kompiliert (GOOS=windows GOARCH=amd64). Ein kurzes Python-Skript liest den Eintrag der Zertifikatstabelle vor und nach dem Signieren:

python3 pe.py hello.exe show; python3 pe.py hello-signed.exe show
file size 1651712, certificate table at offset 0, size 0
file size 1660952, certificate table at offset 1651712, size 9240
WIN_CERTIFICATE: dwLength 9240, wRevision 0x0200, wCertificateType 0x0002

Die 9.240 Byte sind die Signatur, die Zertifikatskette und das Zeitstempel-Token. Eine Liste der Object Identifier in der extrahierten Signatur zeigt die Authenticode-spezifischen Teile neben den üblichen aus CMS:

openssl asn1parse -inform DER -in sig.der | grep -o 'OBJECT *:[^ ]*' | sort | uniq -c (Auszug)
      1 OBJECT            :1.3.6.1.4.1.311.2.1.11
      1 OBJECT            :1.3.6.1.4.1.311.2.1.12
      1 OBJECT            :1.3.6.1.4.1.311.2.1.15
      2 OBJECT            :1.3.6.1.4.1.311.2.1.4
      1 OBJECT            :1.3.6.1.4.1.311.3.3.1
      2 OBJECT            :id-smime-ct-TSTInfo
      2 OBJECT            :messageDigest
      2 OBJECT            :signingTime

1.3.6.1.4.1.311.2.1.4 ist SpcIndirectDataContent, …2.1.15 die darin enthaltenen PE-Image-Daten, …2.1.11 und …2.1.12 der Statement-Typ sowie Programmname und URL, und 1.3.6.1.4.1.311.3.3.1 das Zeitstempel-Token nach RFC 3161 mit dem Content Type id-smime-ct-TSTInfo, gespeichert als nicht signiertes Attribut.

Zertifikate und Schlüssel im Jahr 2026

Für öffentlich vertrauenswürdige Codesignaturzertifikate gelten die Code Signing Baseline Requirements (CSBR) des CA/Browser Forums, Version 3.11.0 vom 16. Juni 2026. Diese Regeln verändern, wie du deine Builds aufsetzt:

  • Hardwareschlüssel für alle. „Ab dem 1. Juni 2023“ muss der private Schlüssel des Zertifikatsnehmers in einem Hardware-Kryptomodul erzeugt, gespeichert und verwendet werden, das mindestens nach FIPS 140-2 Level 2 oder Common Criteria EAL 4+ zertifiziert ist (§6.2.7.4.1). Zulässig sind ein Hardwaremodul, das du selbst betreibst, ein Cloud-Schlüsseldienst, dessen HSM diese Anforderung erfüllt und dessen Abonnement so „konfiguriert ist, dass alle Zugriffe, Vorgänge und Konfigurationsänderungen protokolliert werden“, oder ein Signaturdienst. Die CA muss das prüfen, zum Beispiel indem sie ein Token mit vorab erzeugtem Schlüssel verschickt oder eine Schlüsselattestierung prüft (§6.2.7.4.2). Vor 2023 galt das nur für EV-Zertifikate.
  • Schlüssellängen. RSA-Schlüssel mit mindestens 3072 Bit oder ECDSA auf P-256, P-384 oder P-521 (§6.1.5.2).
  • Laufzeit. Höchstens 460 Tage für Zertifikate, die am oder nach dem 1. März 2026 ausgestellt werden; davor 39 Monate (§6.3.2).
  • Zeitstempeldienste. TSA-Zertifikate gelten höchstens 135 Monate, und ihre Schlüssel dürfen 15 Monate ab notBefore verwendet werden; Zeitstempel-Tokens mit SHA-1 sind seit April 2022 verboten.

Microsoft setzt eigene Anforderungen obendrauf. Seit Windows 10 Version 1607 müssen Kernelmodustreiber von Microsoft über das Windows Hardware Dev Center signiert werden; mit einem kommerziellen Zertifikat allein lädt Windows also keinen Treiber (Richtlinie zur Treibersignatur). Und EV bedeutet nicht mehr „keine SmartScreen-Warnung“: Laut Microsofts Seite zum Status der Verteilungsfeatures „gewährt kein Zertifikatstyp (OV oder EV) eine sofortige Umgehung“, seit das Trusted Root Program 2024 geändert wurde; die Reputation sammelt sich pro Datei-Hash und pro Identität des Signierers an.

Microsofts verwalteter Dienst Artifact Signing (vormals Trusted Signing) bewahrt die Schlüssel in HSMs auf, die Microsoft betreibt, und stellt Zertifikate aus, die „täglich erneuert werden und nur 72 Stunden gültig sind“ (Zertifikatverwaltung). Nur der Digest verlässt deinen Build-Rechner. Bei so kurzlebigen Zertifikaten ist eine Signatur ohne Zeitstempel drei Tage später wertlos. Zwei Details von derselben Seite: Weil sich das Zertifikat jeden Tag ändert, bekommt jedes Zertifikatsprofil eine dauerhafte eigene EKU unter 1.3.6.1.4.1.311.97., an die sich das Vertrauen binden lässt (etwa in Richtlinien zur Anwendungssteuerung). Und die CAs des Dienstes werden aus der CCADB entfernt und sind nicht mehr im Microsoft Trusted Root Program, „verbleiben aber auf der Windows Platform Trust List“: Windows vertraut ihnen also weiterhin, andere Plattformen womöglich nicht.

Signieren und Prüfen mit osslsigncode

osslsigncode implementiert Authenticode unter Linux und macOS. Im Versuch (Debian-Paket 2.14 in debian:forky-20261005-slim) haben wir eine private Testhierarchie angelegt – eine Root-CA, eine ausstellende CA und ein RSA-3072-Codesignaturzertifikat mit der EKU codeSigning –, das Programm signiert und bei der öffentlichen TSA von DigiCert einen Zeitstempel angefordert:

lab.sh (Auszug): mit Zeitstempel nach RFC 3161 signieren, prüfen
osslsigncode sign -certs chain.pem -key codesign.key -h sha256 -n "Hello" -i "https://example.test/hello" \
  -ts http://timestamp.digicert.com -in hello.exe -out hello-signed.exe
osslsigncode verify -CAfile root.pem -TSA-CAfile /etc/ssl/certs/ca-certificates.crt -in hello-signed.exe

An die TSA geht nur ein Hash der Signatur. Die Ausgabe der Prüfung (die wiederholten Auflistungen der Kette sind gekürzt):

osslsigncode verify (gekürzt)
Message digest algorithm  : SHA256
Current message digest    : ADEAAA5366F711E064A9D54630D1E45520727730ECA3B85423D7570B0D41F7A7 
Calculated message digest : ADEAAA5366F711E064A9D54630D1E45520727730ECA3B85423D7570B0D41F7A7 

Signer's certificate:
	------------------
	Signer #0:
		Subject: CN=Example Software GmbH,O=Example Software GmbH,C=DE
		Issuer : CN=Example Test Code Signing CA,O=Example Test
		Serial : 20ADC33D7219F573885B44765077E96312F72F6D
		Certificate expiration date:
			notBefore : Oct 22 06:32:28 2026 GMT
			notAfter : Nov 21 06:32:28 2026 GMT

Authenticated attributes:
	Signing time: Oct 22 06:32:28 2026 GMT
	Microsoft Individual Code Signing purpose
	Message digest: DE669CE86A064FAEB4DFE8E43E14090CE5912FA036C527C13D5558B0BF110BB7 
	URL description: https://example.test/hello
	Text description: Hello

Countersignatures:
	Timestamp time: Oct 22 06:32:29 2026 GMT
	Signing time: Oct 22 06:32:29 2026 GMT
	Hash Algorithm: sha256
	Issuer: CN=DigiCert Trusted G4 TimeStamping RSA4096 SHA256 2025 CA1,O=DigiCert\, Inc.,C=US
	Serial: 084FDC334F7E454EDBC30F8FF9921835
...
Timestamp Server Signature CRL verification: ok
Timestamp Server Signature verification: ok
Signature verification time: Oct 22 06:32:29 2026 GMT
...
Signature verification: ok

Number of verified signatures: 1
Succeeded

„Current message digest“ ist der in der Signatur gespeicherte Hash, „Calculated“ der aus der Datei neu berechnete. Die eigene „Signing time“ des Signierers ist nur das, was seine Uhr behauptet; die „Timestamp time“ ist von der TSA signiert, und auf sie verlassen sich Verifizierer. osslsigncode hat die Signatur dann zum Zeitpunkt des Zeitstempels geprüft – und genau darum geht es bei der ganzen Konstruktion.

Warum der Zeitstempel zählt

Um das zu zeigen, haben wir im Versuch dasselbe Programm ein zweites Mal ohne -ts signiert und dann beide Dateien so geprüft, als wäre es 60 Tage später und das 30-Tage-Testzertifikat bereits abgelaufen (osslsigncode verify -time):

Prüfung zum Zeitpunkt jetzt + 60 Tage (Zeilen gefiltert, Fehlerort von OpenSSL gekürzt)
hello-signed.exe   Signature verification time: Oct 22 06:32:29 2026 GMT Signature verification: ok Succeeded
hello-nots.exe     ...:PKCS7_verify:certificate verify error:...:Verify error: certificate has expired Signature verification time: Dec 21 06:32:29 2026 GMT Error: certificate has expired ... Signature verification: failed Failed

Mit Zeitstempel findet die Prüfung zum nachgewiesenen Signaturzeitpunkt statt, sodass die Signatur das Signaturzertifikat überdauert (das Zertifikat des Zeitstempel-Responders von DigiCert ist in diesem Durchlauf seinerseits bis November 2037 gültig). Ohne Zeitstempel verwendet die Prüfung die aktuelle Zeit und schlägt an dem Tag fehl, an dem das Zertifikat abläuft – mit demselben Fehler certificate has expired wie bei jedem TLS-Client. Microsoft geht noch weiter: Liegt der Zeitstempel innerhalb der Gültigkeitszeiträume sowohl des Signaturzertifikats als auch des TSA-Zertifikats, ist die Signatur gültig, und „sie bleibt noch lange nach dem Ablauf des Signaturzertifikats und des TSA-Zertifikats gültig (sofern keines der beiden widerrufen wird)“.

Auch der Widerruf ist an die Zeit gebunden. Die CSBR erlauben einer CA, das Feld revocationDate eines Codesignaturzertifikats auf das Datum zu setzen, ab dem es erstmals als ungültig galt – bei Schlüsselkompromittierung auf „das früheste Datum der vermuteten Kompromittierung“ –, und sie halten fest: „Software sollte Objekte, die einen Zeitstempel vor dem Widerrufsdatum enthalten, weiterhin als gültig behandeln“ (§4.9.6). Ein abgeflossener Schlüssel macht deshalb die Signaturen ungültig, deren Zeitstempel nach der Kompromittierung liegt, nicht jedes Release, das du je ausgeliefert hast.

Was eine Signatur ungültig macht und was nicht

Zwei Änderungen, geprüft mit demselben Befehl:

Ein Byte Code geändert (Offset 4096), dann 64 Byte innerhalb der Zertifikatstabelle angehängt
### one byte of code changed
Signature verification: failed
Message digest algorithm  : SHA256
Current message digest    : ADEAAA5366F711E064A9D54630D1E45520727730ECA3B85423D7570B0D41F7A7 
Calculated message digest : E754D0A7C1AB0CA1716866C67046BA54D2C195CDCBA4A182D56DD5C57FCD37B6     MISMATCH!!!
Failed
### extra bytes inside the certificate table
appended 64 bytes inside the certificate table: size 9240 -> 9304
Warning: invalid PE checksum
Calculated message digest : ADEAAA5366F711E064A9D54630D1E45520727730ECA3B85423D7570B0D41F7A7 
Timestamp Server Signature verification: ok
Signature verification: ok
Succeeded

Bei der zweiten Datei haben wir den Eintrag der Zertifikatstabelle und die Länge in WIN_CERTIFICATE um 64 Byte vergrößert und dort Daten abgelegt. Weil die Tabelle vom Hash ausgenommen ist, besteht die Signatur die Prüfung weiterhin; nur die PE-Prüfsumme, die ebenfalls ausgenommen ist, stimmt nicht mehr. Genau dieser Aufbau steckt hinter CVE-2013-3900: Installern, die ihre Konfiguration vom Ende der eigenen Datei lesen, ließ sich die Payload eines Angreifers unterschieben, ohne die Signatur ungültig zu machen. Microsoft hat das 2013 behoben, aber nur als Opt-in über die Registry-Einstellung EnableCertPaddingCheck, und in der Neuveröffentlichung von 2022 heißt es, Microsoft „plant nicht, das strengere Prüfverhalten standardmäßig durchzusetzen“. CISA hat die CVE im Januar 2022 in ihren Katalog „Known Exploited Vulnerabilities“ aufgenommen. Die Lektion für die Softwareentwicklung: Lies nie Daten aus dem Bereich der Zertifikatstabelle deiner eigenen ausführbaren Datei, und setze EnableCertPaddingCheck auf den Rechnern, die du verwaltest.

Unter Windows: signtool

SignTool aus dem Windows SDK ist die Referenzimplementierung. Aktuelle Versionen verlangen die Digest-Algorithmen ausdrücklich: „Der SignTool-Befehl sign erfordert die Option für den Digest-Algorithmus der Datei (/fd) und die Option für den Digest-Algorithmus des Zeitstempels (/td)“ (SignTool). Liegt das Zertifikat im Zertifikatsspeicher von Windows und der Schlüssel auf einem Token, lauten die üblichen Befehle:

signtool (aus Microsofts Dokumentation; hier nicht ausgeführt)
signtool sign /n "Example Software GmbH" /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /d "Hello" MyApp.exe
signtool verify /pa /tw /v MyApp.exe
  • /n oder /sha1 wählt das Zertifikat aus; der Schlüssel bleibt im Token oder HSM hinter dessen CSP oder KSP (/csp und /kc benennen sie ausdrücklich).
  • /tr fordert den Zeitstempel bei einem Server nach RFC 3161 an; das ältere /t nutzt das veraltete Authenticode-Zeitstempelprotokoll.
  • verify /pa verwendet die Standardrichtlinie für Authenticode; ohne die Option wendet SignTool die Treiberrichtlinie an und lehnt gewöhnliche Signaturen ab. /tw warnt, wenn eine Signatur keinen Zeitstempel hat – das gehört in jede Release-Pipeline.
  • /dlib und /dmdf binden eine Bibliothek ein, die den Digest remote signiert; Artifact Signing liefert eine mit, und das Open-Source-Tool Jsign (Apache-2.0, Version 7.6) leistet dasselbe auf jedem Betriebssystem mit AWS KMS, Google Cloud KMS, Azure Key Vault, Tokens nach PKCS #11 und YubiKeys.

Auf der prüfenden Seite liefert Get-AuthenticodeSignature in PowerShell den Status und den Signierer, und die Registerkarte „Digitale Signaturen“ im Windows-Explorer zeigt den Zeitstempel.

Signieren in der Build-Pipeline

Mit hardwaregebundenen Schlüsseln liegt auf dem Build-Server keine Schlüsseldatei mehr. Drei Varianten funktionieren:

  1. Ein Cloud-Schlüsseldienst: Erzeuge den Schlüssel in AWS KMS, Google Cloud HSM oder Azure Key Vault Premium, hol dir das Zertifikat von einer CA, die die Schlüsselattestierung oder den Audit-Bericht dieses Dienstes akzeptiert, und signiere den Digest remote mit Jsign oder einem Tool des Anbieters. Die Pflicht zum Audit-Log ist Teil der CSBR; Signieren mit Cloud-KMS zeigt, welche Logs standardmäßig ausgeschaltet sind.
  2. Ein verwalteter Signaturdienst wie Artifact Signing oder der Dienst einer CA: Der Dienst verwahrt Schlüssel und Zertifikat, du schickst Digests.
  3. Ein Token auf einem dedizierten Signaturrechner, erreichbar nur aus dem Release-Job. Einfach, aber dann liegt ein einzelner Rechner auf deinem kritischen Pfad.

Signiere in jedem Fall nur Release-Artefakte, aus einem geschützten Branch und mit Zeitstempel, und halte Datei-Hash und Identität des Signierers in den Release Notes fest, damit Nutzer und EDR-Tools prüfen können, was du ausgeliefert hast.

Häufige Fehler

  • Kein Zeitstempel. Alles funktioniert, bis das Zertifikat abläuft; danach zeigt jede installierte Kopie eine ungültige Signatur. Bei 72-Stunden- oder 460-Tage-Zertifikaten kommt dieser Tag bald.
  • SHA-1 irgendwo. Ohne /fd wurde früher standardmäßig SHA-1 verwendet; aktuelle SignTool-Versionen lehnen das ab, aber ältere Skripte aus Foren enthalten noch /fd sha1 oder /t.
  • Falsche Prüfrichtlinie. signtool verify ohne /pa meldet eine gültige kommerzielle Signatur als nicht vertrauenswürdig.
  • Ein Zertifikat ohne die EKU codeSigning oder ein zweckentfremdetes TLS-Zertifikat. Verifizierer lehnen es ab, so wie OpenSSL unsuitable certificate purpose meldet.
  • Fehlendes Zwischenzertifikat. Nimm die vollständige Kette in die Signatur auf (-certs chain.pem oder das auf dem Signaturrechner installierte Zwischenzertifikat der CA), sonst hängt die Prüfung davon ab, was zufällig auf dem Rechner des Nutzers vorhanden ist.
  • Erwarten, dass EV SmartScreen zum Schweigen bringt. Reputation baut sich heute mit der Zeit auf; signiere jedes Release mit derselben Identität, damit sie von Release zu Release erhalten bleibt.
  • Daten vom Ende der eigenen signierten Datei lesen (Installer, selbstentpackende Archive): Dieser Bereich ist nicht von der Signatur abgedeckt.

Quellen