Authenticode and Catalog Files: The Crypto Foundation Under WDAC
Every Windows trust decision -- UAC, SmartScreen, WDAC, kernel-driver loading -- bottoms out on the same PKCS#7 SignedData envelope shipped in IE 3 in August 1996. Here is the byte-level reason.
Permalink1. The verified-publisher question
On 17 June 2010, Sergey Ulasen and his colleagues at VirusBlokAda in Minsk began circulating a sample of a worm that would, a month later, be named Stuxnet [1][2]. Two of its kernel-mode components, mrxcls.sys and mrxnet.sys, were signed -- properly, by Authenticode-conformant certificates issued to Realtek Semiconductor Corp. and shortly afterwards by JMicron Technology Corp. [2][3]. The Windows kernel loaded them because the certificate chains validated. The chains validated because, cryptographically, nothing was wrong.
That sentence is the lens for everything in this article. Microsoft's code-identity system did its job exactly as designed, and a piece of state-grade sabotage walked through it. The next forty minutes of reading reconstruct what the kernel checks before loading a driver, why those checks could not have caught Stuxnet, and what Microsoft layered on top during the next fourteen years so that the next stolen Realtek private key has less reach.
Where Authenticode shows up
Most Windows users meet Authenticode without realising it. The User Account Control dialog that says "Verified publisher: Microsoft Windows" instead of "Publisher: Unknown" is the user-visible end of a long cryptographic chain that bottoms out in a PKCS#7 / CMS SignedData envelope wrapped inside a WIN_CERTIFICATE record at the end of the PE file [4][5]. The same plumbing is queried by SmartScreen, by App Control for Business (the 2024 rename of Windows Defender Application Control) [6], by ci.dll at kernel-driver load [7], and by Windows Update during servicing. They all read the same bytes in the certificate table; the verdicts differ only in which fields they consult and which policy they overlay.
Diagram source
flowchart TD
SD["PKCS#7 / CMS SignedData
(inside WIN_CERTIFICATE)"]
UAC["UAC
'Verified publisher'"]
SS["SmartScreen
reputation lookup"]
WDAC["App Control for Business (WDAC)
rule evaluation"]
KMCS["ci.dll / KMCS
kernel-driver load"]
SD --> UAC
SD --> SS
SD --> WDAC
SD --> KMCS Every Windows trust statement -- UAC, SmartScreen, App Control for Business, kernel-mode driver loading -- is a query against the same small set of structures inside the PE certificate table. Once you can read those structures, you can predict every later trust decision the OS makes.
What you will be able to do by the end of this article
By the end of section 7 you should be able to decode every line of signtool verify /v /pa /all output and explain, in one paragraph, why Stuxnet still loaded under a fully patched Windows 7 kernel. By the end of section 11 you should be able to run certutil -CatDB, New-CIPolicyRule -FilePath ... -Level FilePublisher, and certutil -hashfile and explain what every byte of their output corresponds to in the on-disk structure.
Stuxnet's kernel components loaded because the chain validated. The chain validated because, cryptographically, nothing was wrong. To understand why that sentence is true -- and what Microsoft has done in the fourteen years since to keep the next stolen Realtek certificate from getting as far -- we have to start in August 1996.
2. 1996: PKCS#7, ActiveX, and the original sin of downloadable code
Counterintuitively, Authenticode was not invented to sign Windows binaries. It was invented to sign downloadable web payloads.
On 7 August 1996, Microsoft and VeriSign jointly announced what their press release called "the first technology for secure downloading of software over the Internet" [8]. The release introduces Authenticode as a feature of Internet Explorer 3 beta 2, names Hank Vigil ("general manager of the electronic commerce group at Microsoft") and Stratton Sclavos ("president and CEO" of VeriSign), and explicitly anchors the design in open standards: "Authenticode and VeriSign's Digital ID service support Internet standards, including the X.509 certificate format and PKCS #7 signature blocks" [8]. Six days later, on 13 August 1996, Internet Explorer 3 itself shipped as RTM for Microsoft Windows [9].
The original motivating problem was ActiveX. An ActiveX control was a downloadable COM binary that the browser would load in-process; without a signature, the browser had no idea who built it. The April 1996 W3C submission that preceded Authenticode is described in the press release as a "code-signing proposal supported by more than 40 companies" [8] The 40+ company W3C signatory list is the institutional fact that made third-party CA participation possible from day one and seeded the modern multi-vendor code-signing economy. None of the architectural decisions that followed -- catalog signing, RFC 3161 timestamping, EV certificates -- would have been viable inside a single-vendor PKI. . Anchoring the design in X.509 and PKCS#7 instead of inventing a Microsoft-only signature format is the choice that made everything afterwards possible.
PKCS#7 was already there
By 1996, the envelope part of the design was solved. RSA Laboratories had published PKCS #7 v1.5 in November 1993 as part of the Public-Key Cryptography Standards series [10]; in March 1998 the IETF republished it verbatim as RFC 2315, "Cryptographic Message Syntax Version 1.5," authored by Burt Kaliski [10]. The same envelope evolved further: the IETF rebranded it as Cryptographic Message Syntax (CMS) and shipped progressively richer versions through RFCs 2630 (1999), 3369 (2002), 3852 (2004), and 5652 (2009) [11]. Modern Authenticode parsers consume the CMS dialect, but the on-disk envelope structure has barely moved in thirty years.
The ASN.1 envelope -- originally PKCS#7 v1.5 (Kaliski, 1993; republished as RFC 2315 in 1998), now generalised as CMS in RFC 5652 -- that carries the signature, signed and unsigned attributes, and the chain of X.509 certificates inside the Authenticode certificate-table entry [11].
Authenticode is, in one sentence, "PKCS#7 SignedData carrying a Microsoft-defined content type that hashes the PE file in a specific repeatable way" [12]. The asymmetric signature inside that envelope is RSA, the public-key system Rivest, Shamir, and Adleman published in 1978 [13], built on the Diffie-Hellman digital-signature concept introduced in 1976 [14]. None of that primitive cryptography has changed since. Everything that has changed sits around the envelope: the algorithms it carries, the catalog store that lets Microsoft sign tens of thousands of files at once, the timestamp tokens that pin a signing moment in time.
Diagram source
flowchart LR
DH["Diffie-Hellman (1976)
digital-signature concept"] --> RSA["RSA (1978)"]
RSA --> P7["PKCS#7 v1.5
(RSA Labs, 1993)"]
P7 --> R2315["RFC 2315 (1998)"]
R2315 --> R2630["RFC 2630 (1999)"]
R2630 --> R3369["RFC 3369 (2002)"]
R3369 --> R3852["RFC 3852 (2004)"]
R3852 --> R5652["RFC 5652 / CMS
(2009)"]
P7 --> AC["Authenticode
(IE3, August 1996)"]
AC --> WC["WIN_CERTIFICATE
in modern PE"] From one click to four trust decisions
The original UX of Authenticode in IE 3 was a modal trust prompt. The user saw a dialog ("Do you want to install and run [name] signed and distributed by [publisher]?") and clicked Yes or No. The signature was checked once, and that was the entire trust decision. By 2026, the same SignedData envelope feeds at least four entirely different trust subsystems -- UAC, SmartScreen, App Control for Business, kernel-mode code integrity -- and most of the time the user clicks nothing at all.
That layering is what the rest of this article is about. Thirty years on, the on-disk bytes have barely changed. The certificate table at the end of every signed Windows binary still carries a PKCS#7 SignedData envelope, and at the head of that envelope is the same content type -- SpcIndirectDataContent -- Microsoft defined in 1996. What has changed is everything around it: the algorithms inside the envelope, the catalog store, the timestamp tokens, the WDAC policy layer on top. Let's open the envelope and look.
3. Anatomy on disk: WIN_CERTIFICATE, PKCS#7 SignedData, SpcIndirectDataContent
Where does the signature actually live in a signed .exe? Most engineers can guess "the end of the file." Fewer can name the data directory entry, fewer still the wrapper structure, and almost nobody volunteers the exact ASN.1 content type. Four nesting levels matter. Walk them in order and the whole rest of the architecture starts making sense.
Level 1: the PE certificate table
The PE optional header carries a DataDirectory[16] array. Entry index 4, IMAGE_DIRECTORY_ENTRY_SECURITY, points at the certificate table -- an offset and size into the file [5]. Unlike every other data directory entry, the certificate table is the only one whose offset is a file offset, not a relative virtual address; the certificate table is never mapped into memory at load time.
Inside that offset+size region is a sequence of WIN_CERTIFICATE records, each laid out as:
typedef struct _WIN_CERTIFICATE {
DWORD dwLength; // total length of this record, including header
WORD wRevision; // WIN_CERT_REVISION_2_0
WORD wCertificateType; // WIN_CERT_TYPE_PKCS_SIGNED_DATA
BYTE bCertificate[ANYSIZE_ARRAY]; // the DER-encoded blob
} WIN_CERTIFICATE;
For Authenticode-signed Windows binaries, wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA (constant value 0x0002), and bCertificate[] is a DER-encoded CMS / PKCS#7 SignedData blob [12]. Multiple WIN_CERTIFICATE records are legal; this is how a single binary can carry both a SHA-1 (legacy) and a SHA-256 (modern) signature, or a dual-signed binary carrying both a primary and a nested secondary embedded signature (via the unsignedAttrs nested-signature mechanism).
Level 2: the CMS SignedData envelope
Decoding bCertificate produces an ASN.1 SEQUENCE describing a CMS ContentInfo whose content type is signedData (OID 1.2.840.113549.1.7.2). Inside that is the SignedData structure proper [11]:
version-- an integer, typically 1 or 3.digestAlgorithms-- the set of hash algorithms used by any signer (commonlysha256).encapContentInfo-- the content the signers are signing over. This is the field that matters.certificates-- the X.509 chain certificates needed to validate the signers.crls-- optional, almost never populated inline.signerInfos-- one or moreSignerInfostructures, each with the actual signature bytes plus signed and unsigned attributes.
Each SignerInfo carries the signing certificate identifier, a set of signedAttrs (whose digest is what gets signed), an encryptedDigest (the actual signature bytes), and a set of unsignedAttrs. The single most important unsigned attribute, in practice, is the RFC 3161 TimeStampToken -- the counter-signature that pegs the signing event to a moment in time. We will come back to that in section 5.
Level 3: SpcIndirectDataContent
The encapContentInfo.eContentType for Authenticode is 1.3.6.1.4.1.311.2.1.4 -- the OID Microsoft registered for SpcIndirectDataContent. Inside, the eContent is a Microsoft-specific ASN.1 structure [12]:
SpcIndirectDataContent ::= SEQUENCE {
data SpcAttributeTypeAndOptionalValue,
messageDigest DigestInfo
}
SpcAttributeTypeAndOptionalValue ::= SEQUENCE {
type OBJECT IDENTIFIER, -- 1.3.6.1.4.1.311.2.1.15 for PE images
value [0] EXPLICIT ANY DEFINED BY type OPTIONAL
}
DigestInfo ::= SEQUENCE {
digestAlgorithm AlgorithmIdentifier,
digest OCTET STRING
}
For a PE binary, data.type is 1.3.6.1.4.1.311.2.1.15 (SPC_PE_IMAGE_DATAOBJ) and data.value carries a SpcPeImageData structure describing what kind of image this is (32-bit, 64-bit, importable, executable). The messageDigest.digest is the Authenticode hash of the PE file [12]. That hash is not SHA-256 over the file bytes.
Microsoft's eContentType registered under OID 1.3.6.1.4.1.311.2.1.4. Its messageDigest field holds the Authenticode hash of the signed artefact, and its data field describes what kind of artefact it is (PE image, MSI, script). The fact that this structure signs a hash rather than a file is what makes catalog signing possible [12].
Level 4: the Authenticode hash and its four exclusions
The Authenticode hash is computed over the PE file with four specific byte ranges excluded [12]:
| Excluded region | Why excluded | Spec reference |
|---|---|---|
OptionalHeader.CheckSum (4 bytes) | The OS recomputes the optional-header checksum when servicing; signing over it would make every signature invalidate at first patch. | Authenticode_PE.docx §3.1 [12] |
DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY] (8 bytes) | The pointer to the certificate table itself moves when a signature is added; signing over the pointer is a chicken-and-egg loop. | Authenticode_PE.docx §3.1 [12] |
| The certificate-table bytes themselves | Same chicken-and-egg loop -- the signature cannot sign itself. | Authenticode_PE.docx §3.1 [12] |
| File-alignment padding after each section | Padding can be different on different builds for harmless reasons (alignment, build-tool quirks); signing over it would punish those harmless differences. | Authenticode_PE.docx §3.1 [12] |
The PE digest computed over the file with four regions excluded: the optional-header CheckSum field, the IMAGE_DIRECTORY_ENTRY_SECURITY data-directory entry, the certificate-table bytes themselves, and the file-alignment padding after each section. Because the excluded regions include the certificate-table area, the same hash remains valid after the signature is appended [12].
The exclusion of the certificate-table bytes is the design move that makes the whole architecture work. The Authenticode hash is computed first, signed, and then the signature is appended into the very region the hash excluded. After appending, the hash is still valid; verifying simply recomputes the hash with the same four regions excluded and compares.
ASN.1 DER's tag-length-value shape means that, given enough patience, you can decode every level of the certificate table with nothing but a hex dump. This accessibility is also why parser bugs are particularly damaging: a verifier that re-encodes or normalises before hashing can be tricked into hashing different bytes than the bytes that get loaded -- the structural failure mode at the bottom of CVE-2013-3900 [15].A separate, smaller hash per 4 KiB page
Authenticode supports an optional signed attribute, SpcPeImagePageHashes2, with OID 1.3.6.1.4.1.311.2.3.2 (SHA-256). It carries a sequence of (RVA, hash) pairs, one hash per 4 KiB page of the PE image [12]. The older 1.3.6.1.4.1.311.2.3.1 SHA-1 variant is effectively deprecated. Under Hypervisor-Protected Code Integrity (HVCI), the page hashes are validated at demand-fault time: when the OS faults in a page from disk, HVCI hashes the page and compares it to the signed page-hash entry before mapping the page as executable. Whole-file integrity checking at load is not the same as runtime integrity checking at fault; page hashes are what closes that gap. ARM64 Windows configurations have used 4 KiB native pages on the systems that ship Authenticode page-hash enforcement to date. The page-hash attribute encodes RVAs into the on-disk image, so any future move to 16 KiB or 64 KiB page granularity would require a corresponding spec revision.
An optional signed attribute (OID 1.3.6.1.4.1.311.2.3.2 for SHA-256) carrying a sequence of (RVA, SHA-256) pairs, one per 4 KiB page of the PE image. The hashes are checked at demand-fault time by HVCI / Code Integrity, not just at load time [12].
The whole nest, in one picture
Diagram source
flowchart TD
PE["PE file on disk"]
OH["Optional header"]
DD["DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY] (entry 4)"]
WC["WIN_CERTIFICATE record
(dwLength, wRevision, wCertificateType, bCertificate[])"]
SD["PKCS#7 / CMS SignedData"]
Certs["certificates: X.509 chain"]
SI["SignerInfo"]
Sa["signedAttrs"]
SIDC["encapContentInfo: SpcIndirectDataContent"]
SPI["data: SpcPeImageData (SPC_PE_IMAGE_DATAOBJ)"]
MD["messageDigest: Authenticode hash"]
PH["SpcPeImagePageHashes2 (optional)"]
ED["encryptedDigest: signature bytes"]
Ua["unsignedAttrs"]
TST["RFC 3161 TimeStampToken
(OID 1.2.840.113549.1.9.16.2.14)"]
PE --> OH
OH --> DD
DD --> WC
WC --> SD
SD --> Certs
SD --> SI
SI --> Sa
Sa --> SIDC
SIDC --> SPI
SIDC --> MD
SIDC --> PH
SI --> ED
SI --> Ua
Ua --> TST Try it yourself
# Decode the four nesting levels of an Authenticode signature.
# Requires: pip install pefile asn1crypto
import pefile
from asn1crypto import cms
PE_PATH = r"C:\Windows\System32\notepad.exe" # any signed PE
pe = pefile.PE(PE_PATH, fast_load=True)
pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']])
cert_dir = pe.OPTIONAL_HEADER.DATA_DIRECTORY[4]
if cert_dir.VirtualAddress == 0:
print("No certificate table -- file is unsigned (or catalog-signed elsewhere).")
else:
raw = pe.__data__[cert_dir.VirtualAddress: cert_dir.VirtualAddress + cert_dir.Size]
# WIN_CERTIFICATE header: dwLength(4) wRevision(2) wCertificateType(2)
import struct
dw_length, w_revision, w_cert_type = struct.unpack("<IHH", raw[:8])
pkcs7_blob = raw[8: dw_length]
print(f"WIN_CERTIFICATE: dwLength={dw_length} wRevision=0x{w_revision:04x} wCertificateType=0x{w_cert_type:04x}")
info = cms.ContentInfo.load(pkcs7_blob)
signed_data = info["content"]
encap = signed_data["encap_content_info"]
print(f"eContentType: {encap['content_type'].native}") # expect SpcIndirectDataContent OID 1.3.6.1.4.1.311.2.1.4
# Authenticode SpcIndirectDataContent is not a standard CMS payload, so
# asn1crypto returns it as raw bytes -- decode the messageDigest by hand.
inner = encap["content"].parsed if encap["content"].native else encap["content"].contents
print(f"Inner bytes (first 32 hex): {bytes(inner)[:32].hex()}") Press Run to execute.
We can now describe, byte for byte, what a signed PE looks like. The on-disk shape is precise enough that a parser flaw in WinVerifyTrust -- the one that became CVE-2013-3900 in December 2013 [15][16] -- could let an attacker append bytes inside the certificate-table region without invalidating the signature, because the verifier happily skipped over them. To understand why such a flaw exists, why Microsoft still has not made the fix default-on twelve years later, and why no fewer than four named incidents drove the kernel-mode signing regime toward its current shape, we have to walk the evolution generation by generation.
4. Six generations of Windows code signing
Each generation solved a real problem in the previous one. None of them is dead. Catalog signing, introduced as Gen 2, is still load-bearing on every modern Windows install for driver packages and inbox files; embedded Authenticode, the Gen 1 idea, is still how every commercial ISV ships a binary. The generations are layers, not replacements.
Diagram source
gantt
title Six generations of Windows code signing
dateFormat YYYY-MM-DD
axisFormat %Y
section Generations
Gen 1: Embedded Authenticode (IE3) :g1, 1996-08-07, 1999-12-31
Gen 2: Catalog files (.cat) and CatRoot :g2, 1999-01-01, 2005-12-31
Gen 3: KMCS + PatchGuard (x64) :g3, 2005-01-01, 2010-12-31
Gen 4: Hash agility + CVE-2013-3900 :g4, 2010-01-01, 2015-12-31
Gen 5: HW Dev Center portal signing :g5, 2015-07-29, 2021-12-31
Gen 6: WDAC + page hashes + VDB :g6, 2017-01-01, 2026-05-12
section Named incidents
Stuxnet :milestone, 2010-06-17, 0d
Flame :milestone, 2012-05-28, 0d
MS13-098 :milestone, 2013-12-10, 0d
KMCS cross-sign cutoff :milestone, 2015-07-29, 0d
ShadowHammer :milestone, 2019-03-25, 0d
VDB default-on (Win11 22H2) :milestone, 2022-09-20, 0d
Bitwarden CLI npm :milestone, 2026-04-22, 0d Gen 1 (1996-1999): per-file embedded Authenticode
The original design. Each ISV holds its own private key, signs each binary as it ships, the signature lives inside the PE certificate table. The IE 3 trust prompt is the only consumer. It works. It does not scale to operating-system inbox files. Microsoft cannot hold every IHV's private key (the IHV would have to mail its source binary to Redmond to be signed) and an IHV cannot sign Microsoft's own binaries (Microsoft will not surrender its private key). Worse, the spec property "single-byte change invalidates the signature" [17] means that even a corrected misspelling in an INF file would break the signature on the driver package the INF is paired with. Embedded Authenticode is the right answer for an ISV that ships a single product; it is the wrong answer for an OS that ships tens of thousands of files.
Gen 2 (1999-2005): catalog files and the CatRoot store
The conceptual breakthrough is to sign a list of hashes, not a file -- the verbatim Microsoft Learn definition of a catalog file is reproduced as a PullQuote in §5 [17][18]. The OS installs the .cat to %SystemRoot%\System32\CatRoot\{GUID}\ [17], indexes the member hashes via the CryptSvc service, and when WinVerifyTrust is asked to validate a PE without an embedded signature it computes the Authenticode hash and asks the catalog database whether any installed catalog covers that hash [4]. Starting with Windows 2000, INF files use a single CatalogFile directive that lets the same package install identically on every Windows version it supports [17].
Catalogs fix scale: Microsoft signs one .cat, that catalog covers thousands of driver-package files, and any one-byte INF correction rebuilds the catalog without touching the per-file signatures. They do not yet fix kernel-mode trust on 32-bit Windows, where unsigned drivers still load.
Gen 3 (2005-2010): Kernel-Mode Code Signing on x64
PatchGuard (Kernel Patch Protection) shipped first, on x64 Windows Server 2003 SP1 in April 2005 [19], to prevent runtime patching of kernel structures. A year and a half later, with x64 Windows Vista (RTM November 2006, GA January 2007) [20], Microsoft made kernel-mode driver loading conditional on a valid Authenticode signature chaining to the Microsoft Code Verification Root -- the first Windows release to enforce KMCS at load. The Microsoft Learn KMCS policy page is explicit that the regime applies to Windows Vista and later [7]. Cross-signing -- a third-party CA's intermediate cross-signed by a Microsoft anchor -- let independent driver vendors continue shipping without Microsoft holding their keys [7]. KMCS works exactly as intended on x64. Then someone steals a private key.
On 17 June 2010, the Stuxnet samples carrying mrxcls.sys and mrxnet.sys -- signed by a legitimately issued Realtek Semiconductor Corp. certificate, and shortly afterwards by a JMicron Technology Corp. certificate -- are first uncovered [1][2]. VeriSign revokes the Realtek certificate on 16 July 2010 per the Symantec dossier [2]. Two years later, on 28 May 2012, Flame is publicly disclosed by the MAHER CERT, Kaspersky, and CrySyS Lab [21]. The Flame authors forged a Microsoft-issued sub-CA, the Microsoft Enforced Licensing Intermediate PCA, by mounting an MD5 chosen-prefix collision [22][23]. Microsoft Security Advisory 2718704 (published 3 June 2012, updated 13 June 2012) revoked the two intermediate certificates and the matching SHA-1 RA certificate within days of disclosure [23]. The cryptanalytic precedent for the Flame work was the December 2008 rogue-CA result by Alexander Sotirov, Marc Stevens, Jacob Appelbaum, Arjen Lenstra, David Molnar, Dag Arne Osvik, and Benne de Weger -- presented as "MD5 considered harmful today: Creating a rogue CA certificate" at 25C3 in Berlin [24], later published as the Crypto 2009 best paper "Short Chosen-Prefix Collisions for MD5 and the Creation of a Rogue CA Certificate." The rogue-CA cryptanalysis is sometimes mis-cited as Eurocrypt 2009. The correct venue is Crypto 2009 (Santa Barbara), where the paper won the best-paper award. The original disclosure was the December 2008 25C3 talk. Stevens later used the same forensic technique to identify the Flame collision in his Crypto 2013 paper Counter-Cryptanalysis [22].
The composite lesson of Gen 3 is uncomfortable: validating the chain does not protect against a stolen private key, and it does not protect against a forged sub-CA certificate either. The signature was, in both cases, cryptographically valid.
Gen 4 (2010-2015): hash agility and CVE-2013-3900
Microsoft moved Authenticode off MD5 (Flame's collision target) and toward SHA-256 as the default hash [25]. On 10 December 2013, MS13-098 patched CVE-2013-3900, a parser flaw in WinVerifyTrust that let an attacker append additional bytes inside the certificate-table region without invalidating the signature [16]. The patch added a stricter parser that rejected the appended-data form. Microsoft did not enable the stricter parser by default.
"Microsoft does not plan to enforce the stricter verification behavior as a default functionality on supported releases of Microsoft Windows." -- NVD, CVE-2013-3900 [15]
The reasoning, as preserved verbatim in the NVD republication, is application compatibility: legitimate installers shipped binaries that had small amounts of extra data appended inside the certificate-table area, and breaking those installers en masse would have been a customer-visible regression. The opt-in registry setting -- HKLM\Software\Microsoft\Cryptography\Wintrust\Config\EnableCertPaddingCheck=1, with a matching Wow6432Node sibling -- has been available on every supported Windows release since December 2013. CISA added CVE-2013-3900 to the Known Exploited Vulnerabilities catalogue on 10 January 2022 with a federal due date of 10 July 2022 [15]. As of this writing, the strict-parser behaviour is still opt-in.
EV (Extended Validation) code signing requirements emerged during the Gen 4–Gen 5 transition. The CA/Browser Forum approved the initial Minimum Requirements for the Issuance and Management of Publicly-Trusted Code Signing Certificates on 22 September 2016, with effective force from 1 February 2017 [26]. The CSBR is commonly cited with a 2017 publication date. The correct framing is that the v1.0 baseline was approved on 22 September 2016 and became effective on 1 February 2017; the v1.1 update in the PKI Consortium mirror dates from the same approval cycle. The current CSBR is v3.8, dated 5 August 2024 [27], with the EV code-signing requirements imported from the older EV Guidelines [27][28]. EV certificates were the first time the key the signature was made with had to live on hardware (a token or HSM) and the only way to produce a signature was to plug the token in. That moves the Stuxnet-style "stolen private key" problem out of the disk-of-the-developer space and into the much smaller "physical theft of a hardware token" space.
Gen 5 (2015-2021): Hardware Developer Center portal signing
On 29 July 2015, Microsoft closed cross-signing for new kernel-mode end-entity certificates. The KMCS policy page is verbatim: "Cross-signed drivers are still permitted if any of the following are true: ... Drivers was signed with an end-entity certificate issued prior to July 29th 2015 that chains to a supported cross-signed CA" [7]. Practically, new kernel drivers now have to go through the Hardware Developer Center: either attestation signing (an EV-cert-signed driver that Microsoft counter-signs, valid for in-house and OEM-channel distribution [29]) or full Windows Hardware Quality Labs (WHQL) signing (HLK-tested, publishable on Windows Update, valid on Vista and later [30]). Attestation-signed drivers cannot be published to Windows Update for retail audiences [30] -- that lever is reserved for WHQL.
By July 2021, most cross-certificates had expired, and the deprecation page is exact: "Most cross-certificates expired in July 2021. You can't use code-signing certificates that chain with expired cross-certificates to create new kernel mode digital signatures for any version of Windows" [31]. Cross-signing for new signatures is fully closed.
Gen 6 (2017-present): App Control for Business, page hashes, the VDB
In 2017 Microsoft introduced Windows Defender Application Control [32], renamed to App Control for Business in 2024 [6]. The policy language defines rule levels (Hash, FileName, Publisher, FilePublisher, WHQL, WHQLPublisher, WHQLFilePublisher, LeafCertificate, PcaCertificate, RootCertificate) [33]; we look at them in detail in section 7. The Vulnerable Driver Blocklist (VDB), seeded in 2019 and shipped as a default-on supplemental deny policy from the Windows 11 2022 Update onward, denies a curated set of known-vulnerable signed kernel drivers [34]. The VDB is automatically enforced whenever memory integrity (HVCI), Smart App Control, or S Mode is active (except on Windows Server 2016) [34]; the blocklist is updated quarterly. Microsoft launched the Vulnerable and Malicious Driver Reporting Center in December 2021 to formalise the intake side of the VDB pipeline [35].
Gen 6 does not invent a new envelope. It treats the existing Authenticode primitives as inputs to a policy engine. The "trust" decision is no longer a single yes/no derived from the certificate chain; it is a composite of cryptographic verdicts and administrator-authored rules. Even so, every named incident continues to fit the same pattern. Operation ShadowHammer (publicly disclosed 25 March 2019) compromised the ASUS Live Update mechanism, distributing trojanised updaters signed with legitimate ASUSTeK certificates -- "over 57,000 Kaspersky users" downloaded them, hosted on liveupdate01s.asus[.]com and liveupdate01.asus[.]com [36]. The signature was valid; the binary was malicious. Seven years later, on 22 April 2026, a malicious version of @bitwarden/cli@2026.4.0 was briefly distributed through npm between 5:57 PM and 7:30 PM Eastern Time as part of the broader Checkmarx supply-chain campaign [37][38][39]. StepSecurity's analysis calls this "the first confirmed supply chain attack where npm's OIDC Trusted Publishing was used to publish a compromised package" [39]. The signature path is different from a PE Authenticode signature -- npm uses its own OIDC-based attestation -- but the lower bound is identical to Stuxnet, fourteen years earlier. Provenance does not imply safety.
| Approach | Year | Idea | Status |
|---|---|---|---|
| Per-binary embedded Authenticode | 1996 | One signature per file, in the certificate table | Active (every commercial ISV) |
| Catalog (.cat) signing | 1999-2000 | Sign a list of hashes; OS-managed CatRoot store | Active (every modern Windows for driver packages and inbox files) |
| KMCS + cross-signing | 2006-2007 | Mandatory chain to Microsoft Code Verification Root on x64 (Vista RTM Nov 2006, GA Jan 2007) | Cross-signing closed for new certs (29 Jul 2015); KMCS still active |
| RFC 3161 timestamping | 2001 | Counter-signature pinning signing time | Default for every well-formed Authenticode signature |
| Hash agility (MD5 -> SHA-256) | 2012-2015 | Replace collision-broken hash algorithm | Active; SHA-256 universal |
| EnableCertPaddingCheck (CVE-2013-3900 strict parser) | 2013 | Reject appended bytes in certificate-table region | Opt-in; CISA KEV-listed since 10 Jan 2022 |
| Hardware Developer Center portal signing | 2015 | Microsoft counter-signs every new kernel driver | Active; cross-signing fully retired by July 2021 |
| WHQL / HLK signing | 2007-present | Driver passes HLK, publishable on Windows Update | Active (recommended retail path) |
| Attestation signing | 2015-present | EV-cert + Microsoft counter-signature; not publishable on WU retail | Active (in-house, OEM channel) |
| Vulnerable Driver Blocklist | 2019-present (default-on 2022) | Deny known-vulnerable signed drivers | Default-on with HVCI / Smart App Control / S Mode; quarterly cadence |
| App Control for Business (WDAC) | 2015-present | Engine shipped as configurable code integrity / Device Guard (2015); renamed Windows Defender Application Control (2017); rebranded App Control for Business (2024). Administrator-authored allow/deny rules over Authenticode primitives. | Active; current production policy language |
Six generations is a lot of layering for what is, at the bottom, the same PKCS#7 SignedData envelope from 1996. The one moment in this lineage that genuinely changed something is small enough to fit in one sentence: the realisation that SpcIndirectDataContent signs a hash, not a file. That single observation produced both catalog signing and RFC 3161 timestamping. Both of them are why Windows code identity scaled and survived to 2026.
5. The two decoupling insights: catalog signing and RFC 3161 timestamping
Both insights are instances of the same move: the signature is not where you think it is. Once you internalise that, the rest of the architecture stops being a sequence of incremental crypto choices and starts being a sequence of policy choices on top of a small set of primitives.
Insight A: catalog signing decouples the hash from the file
Recall from section 3 that SpcIndirectDataContent carries a messageDigest -- a hash -- and a small descriptor of what was hashed. Nothing in that envelope says the hash must come from one specific file. Catalog signing exploits exactly that. A .cat is a degenerate SignedData whose encapsulated content is a CatalogList ASN.1 structure: a sequence of (memberHash, attributes) tuples plus a single Microsoft (or vendor) signature over the whole list [4]. Each memberHash is the same Authenticode hash that would be embedded in the per-file case.
Microsoft Learn's verbatim definition is again exact:
"A digitally signed catalog file (.cat) can be used as a digital signature for an arbitrary collection of files. A catalog file contains a collection of cryptographic hashes, or thumbprints. Each thumbprint corresponds to a file that is included in the collection." -- Microsoft Learn, Catalog Files and Digital Signatures [17]
When a catalog is installed on disk, the OS drops it into %SystemRoot%\System32\CatRoot\{GUID}\ (with staging in CatRoot2) [17]. The CryptSvc service maintains the catalog database -- effectively an index from memberHash -> catalogFile -- and answers lookups from WinVerifyTrust. When a PE without an embedded signature reaches the verifier, the verifier computes the file's Authenticode hash and asks CryptSvc whether any installed catalog contains that hash. If yes, the catalog signer becomes the effective signer for the file [4].
A degenerate PKCS#7 / CMS SignedData whose encapsulated content is a CatalogList of (memberHash, attributes) tuples; a single signature covers the whole list. Catalog files act as detached signatures for an arbitrary set of binaries: any file whose Authenticode hash appears in the list is treated as if it carried the catalog signer's embedded signature [17][4].
The on-endpoint catalog store at %SystemRoot%\System32\CatRoot\{GUID}\ (with staging in CatRoot2) and the Windows service that indexes installed catalog member hashes so WinVerifyTrust can answer "is this Authenticode hash covered by any installed catalog?" [17].
Catalog signing makes three workflows possible that embedded signing alone cannot:
- WHQL signing at OS scale. Microsoft signs a single
.catcovering every Windows inbox file in a build; updates to those files arrive as new catalogs without re-signing each binary. - Trust refresh through Windows Update. Adding new trust without touching any binary -- Microsoft just ships another catalog, and the on-endpoint
CryptSvcextends its index. - Catalog-signing unsigned line-of-business apps. Enterprises with internally built apps that lack their own code-signing infrastructure can use the Package Inspector workflow -- "you can create catalog files for existing apps without requiring access to the original source code or needing any expensive repackaging" [40] -- to wrap a
.cataround the installed binary set and pass App Control rule evaluation without modifying the executable itself.
Diagram source
sequenceDiagram
participant IHV as IHV / vendor
participant HWDC as Microsoft HW Dev Center
participant WU as Windows Update
participant CR as Endpoint CatRoot + CryptSvc
participant CI as ci.dll (kernel)
IHV->>HWDC: Submit driver package + .cat (CAB, EV-cert signed)
HWDC->>HWDC: Counter-sign .cat with Microsoft cert
HWDC-->>IHV: Return Microsoft-signed .cat
IHV->>WU: Publish driver package (if WHQL)
WU->>CR: Deliver signed .cat to endpoint
CR->>CR: Index member hashes in CryptSvc DB
CI->>CR: WinVerifyTrust: hash(X.sys) found in any catalog?
CR-->>CI: Yes -- catalog signer = Microsoft
CI->>CI: Load driver Insight B: RFC 3161 timestamping decouples the signature lifetime from the certificate's validity
Every X.509 end-entity code-signing certificate has a finite validity window -- usually one to three years. Without something extra, a signature would stop verifying the moment the signing certificate expired. That is operationally unacceptable: Microsoft has been shipping Windows binaries since 1996 and cannot reissue every certificate every time something old gets re-installed. RFC 3161 [41] is the answer.
The relevant paragraph from the RFC, verbatim:
"The TSA's role is to time-stamp a datum to establish evidence indicating that a datum existed before a particular time. This can then be used, for example, to verify that a digital signature was applied to a message before the corresponding certificate was revoked thus allowing a revoked public key certificate to be used for verifying signatures created prior to the time of revocation." -- RFC 3161 §1 [41]
Operationally: the signer hashes the original SignerInfo.encryptedDigest, sends that hash to a Trusted Time-Stamping Authority (TSA), and the TSA returns a TimeStampToken -- itself a CMS SignedData -- whose signed content is Hash(originalSignature) || genTime. The signer attaches the token as an unsigned attribute on the original SignerInfo, OID 1.2.840.113549.1.9.16.2.14. Later verifiers can recover genTime from the token, confirm the TSA's signature chains to a trusted root, and decide: was the signing certificate valid at genTime? If yes, the signature is honoured even if the signing certificate has since expired (or, on some verifiers, even if it has since been revoked for reasons that did not retroactively invalidate the pre-revocation signature).
An RFC 3161 service that, given a hash of a signature, returns a CMS-wrapped TimeStampToken countersigning the hash with a trusted signing time (genTime). The token is attached as an unsigned attribute on the original SignerInfo. The TSA's role is to make the signing event verifiable in time even after the signing certificate expires [41].
Diagram source
sequenceDiagram
participant S as Signer (signtool /tr)
participant TSA as RFC 3161 TSA
participant V as Later verifier
S->>S: Compute Authenticode hash, build SignerInfo
S->>TSA: POST hash(SignerInfo.encryptedDigest)
TSA-->>S: TimeStampToken (SignedData over hash || genTime)
S->>S: Attach token as unsigned attribute (OID 1.2.840.113549.1.9.16.2.14)
Note over S,V: ... years later ...
V->>V: Decode TimeStampToken, verify TSA chain
V->>V: Extract genTime
V->>V: Was signing cert valid at genTime? -> trust decision The unifying observation
Both moves untie something that was previously tied:
- Catalog signing unties the signature from a specific file's bytes.
- RFC 3161 unties the signing event from the issuing certificate's validity window.
After these two decouplings, signing at scale and signing for longevity both become tractable, and everything later in the Windows code-signing stack is a policy layer operating on top.
Two decouplings -- catalog signing untying the signature from a specific file, and RFC 3161 timestamping untying the signature from the end-entity certificate's validity window -- are what made Windows code identity scale to OS-sized binaries and survive across decades. Every later layer (KMCS, WDAC, the Vulnerable Driver Blocklist, HVCI) presumes these two primitives are already in place.
// PowerShell exposes catalog-vs-embedded provenance via Get-AuthenticodeSignature.
// On Windows, run the following commands in PowerShell to see the difference between
// an embedded-signed binary (e.g. notepad.exe) and a catalog-signed inbox file
// (e.g. ntoskrnl.exe).
//
// On non-Windows hosts, this snippet illustrates the JSON shape PowerShell returns
// so you can wire the workflow into a cross-platform agent.
const embeddedSignedBinary = {
Path: "C:\\Windows\\System32\\notepad.exe",
// PowerShell: (Get-AuthenticodeSignature notepad.exe).SignatureType -> Authenticode
SignatureType: "Authenticode",
Status: "Valid",
CatalogFile: null,
SignerCertificate: { Subject: "CN=Microsoft Windows, ..." }
};
const catalogSignedInboxFile = {
Path: "C:\\Windows\\System32\\ntoskrnl.exe",
// PowerShell: (Get-AuthenticodeSignature ntoskrnl.exe).SignatureType -> Catalog
SignatureType: "Catalog",
Status: "Valid",
CatalogFile: "C:\\Windows\\System32\\CatRoot\\{F750E6C3-...}\\Microsoft-Windows-Client-Drivers-Package~31bf3856ad364e35~amd64~~10.0.x.y.cat",
SignerCertificate: { Subject: "CN=Microsoft Windows Production PCA 2011, ..." }
};
console.log("Embedded signature:", embeddedSignedBinary.SignatureType, embeddedSignedBinary.CatalogFile);
console.log("Catalog signature: ", catalogSignedInboxFile.SignatureType, catalogSignedInboxFile.CatalogFile); Press Run to execute.
Once you can sign a hash instead of a file, and once you can pin a signing event to a moment in time that outlives the certificate, the rest of the architecture stops being a sequence of crypto choices and starts being a sequence of policy choices: which roots do we trust for ring 0, which file-publisher tuples does this enterprise authorise, which drivers does Microsoft deny by hash? To see those policy choices in operation, watch a single WinVerifyTrust call end to end.
6. A modern WinVerifyTrust call, end to end
A user double-clicks a Microsoft-signed .exe on Windows 11 24H2. HVCI is on, Smart App Control is on, an enterprise App Control policy is loaded. The shell calls ShellExecute. Before the OS hands control to the new process, the kernel's code-integrity stack (ci.dll) and user-mode WinVerifyTrust between them answer the question "is this binary trusted?" in roughly the following seven stages.
Stage 1: read the certificate table
ci.dll reads the optional header, finds DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY], walks the certificate-table region, and enumerates the WIN_CERTIFICATE records. Multiple records may be present (e.g. a SHA-1 record for compatibility with Windows 7 verifiers, and a SHA-256 record for modern ones); the verifier picks the strongest record whose algorithm is allowed by current policy [12][5].
Stage 2: decode the SignedData
For each candidate record with wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA, the verifier DER-decodes bCertificate into a CMS ContentInfo, then into a SignedData structure [11]. The verifier reads signerInfos, picks the signer (usually one), and extracts the signed and unsigned attributes.
Stage 3: verify the content type
The verifier confirms encapContentInfo.eContentType == 1.3.6.1.4.1.311.2.1.4 (SpcIndirectDataContent), then decodes the inner structure and confirms data.type == 1.3.6.1.4.1.311.2.1.15 (SPC_PE_IMAGE_DATAOBJ) [12]. The inner messageDigest is the Authenticode hash this signature claims to cover; the digestAlgorithm says how it was computed.
Stage 4: recompute the Authenticode hash
The verifier re-reads the PE file bytes, applies the four exclusions (CheckSum, the SECURITY data-directory entry, the certificate-table bytes, and section-padding), hashes the remaining bytes with the claimed algorithm, and compares to SpcIndirectDataContent.messageDigest [12]. If they differ, the signature is rejected.
Stage 5: validate page hashes under HVCI
If SpcPeImagePageHashes2 is attached and the running policy includes HVCI, the page-hash table is preserved across the verification call and consulted later by the secure kernel at demand-fault time [12]. The full-file Authenticode hash check is necessary but not sufficient for runtime integrity; pages on disk can be tampered after load by a kernel-level attacker who bypasses file-system protections. Page hashes are what closes that gap by re-checking each page at the moment it is mapped executable.
Stage 6: build the chain
The verifier collects the certificates SET from the SignedData, plus any AIA-fetched certificates needed to complete the chain, and tries to terminate the path at a trusted root. For kernel-mode loads, the legacy anchor is the Microsoft Code Verification Root; for portal-signed drivers, the chain may instead terminate at one of the Microsoft Root Authority anchors. The KMCS policy page describes the Windows 10 1607+ kernel-mode anchors verbatim: "Microsoft Root Authority 2010, Microsoft Root Certificate Authority, Microsoft Root Authority" with Secure Boot on [7]. For user-mode loads, the chain may terminate at any root in the system Trusted Root store; the enterprise's App Control policy narrows the trust further by referencing specific anchors at the RootCertificate / PcaCertificate rule level [33].
The CryptoAPI function (wintrust.dll!WinVerifyTrust) that orchestrates the Authenticode verification pipeline: certificate-table read, SignedData decode, content-type check, Authenticode-hash recomputation, optional page-hash association, chain build, catalog fallback for unsigned PEs, and timestamp validation. It returns a success or specific error code; the caller (UAC, SmartScreen, ci.dll, WDAC) interprets the result against its own policy.
The historical kernel-mode trust anchor whose name appears in Microsoft's KMCS documentation and whose intermediate cross-signed third-party code-signing CAs for pre-July-2015 drivers [7]. Microsoft Learn does not publish a single canonical page with the root's SHA-1 / SHA-256 thumbprint, validity dates, or issuance year; in practice the thumbprint is read by running certutil -store on a recent Windows system.
certutil -store on the running system rather than copy-pasted from a published document.
Stage 7: catalog fallback for unsigned PEs
If the PE has no embedded signature, the verifier computes the Authenticode hash and queries CryptSvc: is this hash a member of any installed catalog under %SystemRoot%\System32\CatRoot\? If yes, the verifier uses the catalog's signer as the effective signer for the PE [17][4]. Cross-system files installed by Windows Update (most drivers, most inbox executables) take this path.
Stage 8: validate the RFC 3161 timestamp
If the unsigned attributes carry a TimeStampToken (OID 1.2.840.113549.1.9.16.2.14), the verifier decodes it, validates the TSA's chain, extracts genTime, and confirms the signing certificate was valid at genTime [41]. This is how a 2010 signature still verifies in 2026: not because the 2010 certificate is still valid, but because a TSA attested at signing time that the signature existed when the certificate was valid.
Stage 9: WDAC policy evaluation
With cryptographic verdicts in hand, the App Control policy engine evaluates the file against the active policy: does any allow rule match, does any deny rule match, including the default-on Vulnerable Driver Blocklist supplemental deny [34]? The matching rule -- by Hash, FileName, Publisher, FilePublisher, WHQL, WHQLPublisher, WHQLFilePublisher, LeafCertificate, PcaCertificate, or RootCertificate level [33] -- decides the final outcome. Audit-mode hits produce event ID 3076; enforcement-mode blocks produce event ID 3077 [42].
Stage 10: legacy parser hardening, if opted in
A hardened environment will also have EnableCertPaddingCheck=1 set [15], enabling the strict parser that rejects the CVE-2013-3900 appended-data form. CISA added the CVE to its Known Exploited Vulnerabilities catalogue on 10 January 2022 with a federal due date of 10 July 2022 [15]; environments subject to federal compliance regimes treat this as mandatory. For practitioners: the registry key needs to be set in both HKLM\Software\Microsoft\Cryptography\Wintrust\Config and the matching Wow6432Node path, because the 32-bit and 64-bit WinVerifyTrust code paths read separate copies. Setting only one and rebooting is one of the more common configuration mistakes in hardened-baseline rollouts.
Diagram source
flowchart TD
Start["ShellExecute / driver load"]
CT["Read PE certificate table"]
Decode["Decode WIN_CERTIFICATE -> CMS SignedData"]
OID{"eContentType =
SpcIndirectDataContent?"}
Hash["Recompute Authenticode hash"]
HashOK{"Hash matches
messageDigest?"}
Chain["Build certificate chain"]
Cat["Catalog fallback?
(if PE unsigned)"]
TS["Validate RFC 3161 token"]
PHash["Associate SpcPeImagePageHashes2
(HVCI fault-time check)"]
Pol["WDAC policy evaluation"]
EPC["EnableCertPaddingCheck
strict parser (if opt-in)"]
Done["LOAD or DENY"]
Start --> CT --> Decode --> OID
OID -->|yes| Hash
OID -->|no| Done
Hash --> HashOK
HashOK -->|yes| Chain
HashOK -->|no| Done
Chain --> Cat --> TS --> PHash --> EPC --> Pol --> Done By the seventh stage of this pipeline, the answer to "is this binary trusted?" is no longer a yes-or-no statement about cryptography. It is a composite of cryptographic verdicts (signature integrity, hash match, chain build, timestamp validity, page hashes) and policy verdicts (allowed by WDAC, not on the blocklist). Authenticode supplies the inputs to a policy; WDAC writes the policy. Let us look at the policy language.
7. WDAC rule levels: Authenticode as policy input, not policy itself
App Control for Business (WDAC) is where the Authenticode primitives finally surface to administrators as policy. The SignerInfo, the subject CN of the leaf certificate, the file's OriginalFileName and ProductVersion from the version resource, the page-hash table, even the choice of catalog signer -- all of them become inputs to a small rule language.
Rule levels: what Authenticode field each level consults
The verbatim rule-level catalogue from Microsoft Learn is [33]:
| Rule level | Authenticode field(s) consulted | Example use case |
|---|---|---|
Hash | Authenticode hash of the file | Pinning a single binary by exact bytes; brittle across patches. |
FileName | OriginalFileName from the PE version resource | Convenience for inbox files; not cryptographic. |
FilePath | Filesystem path | UNC or absolute path; not cryptographic. Use sparingly. |
SignedVersion | Publisher + OriginalFileName + version range | Allow a publisher's binary at a given version or higher. |
Publisher | Issuing CA + leaf-cert subject CN | Allow anything signed by a given vendor under a given CA. |
FilePublisher | Publisher + OriginalFileName + minimum FileVersion | Allow a specific binary from a specific vendor at min version. |
WHQL | The Windows Hardware Quality Labs EKU | Allow any WHQL-signed driver. |
WHQLPublisher | WHQL EKU + leaf-cert subject CN | Allow WHQL drivers from a specific OEM. |
WHQLFilePublisher | WHQL EKU + OriginalFileName + min FileVersion | The strictest driver rule. |
LeafCertificate | Leaf cert subject + issuer | Pin to a specific signing cert. |
PcaCertificate | The PCA (intermediate) cert | Useful for "anything Microsoft-signed" without enumerating leaves. |
RootCertificate | The root anchor | Broadest; usually too coarse. |
Policy options
App Control policies are XML documents with a <Rules> section that toggles broad behavioural options [33]:
0 Enabled:UMCI-- "App Control policies restrict both kernel-mode and user-mode binaries. By default, only kernel-mode binaries are restricted. Enabling this rule option validates user mode executables and scripts" [33].2 Required:WHQL-- "By default, kernel drivers that aren't Windows Hardware Quality Labs (WHQL) signed are allowed to run. Enabling this rule requires that every driver is WHQL signed and removes legacy driver support" [33].8 Required:EV Signers-- documented but, per the same Microsoft Learn page, "This option isn't currently supported." The Required:EV Signers option is in every published rule-options table but never makes it past parsing today. The EV requirement is enforced contractually via the Hardware Developer Center submission gate, not via the rule option. Treat it as documentation of intent rather than runtime enforcement.
The Vulnerable Driver Blocklist is shipped as a supplemental deny policy that overlays the user's primary policy. From Windows 11 22H2 onward it is default-on and automatically enforced under HVCI, Smart App Control, or S Mode [34]. Updates arrive quarterly. The blocklist is deliberately conservative: Microsoft's own documentation acknowledges "It's often necessary for us to hold back some blocks to avoid breaking existing functionality while we work with our partners who are engaging their users to update to patched versions" [34].
The post-2024 rename of Windows Defender Application Control [6]; a code-integrity policy language that consumes Authenticode primitives (chain, leaf-cert subject, OriginalFileName, version, WHQL EKU, page-hash table, embedded-vs-catalog provenance) as inputs to administrator-authored allow and deny rules.
A WDAC rule level that allows or denies a binary if it is signed by a given Publisher (issuing CA + leaf-cert subject CN) and the PE's OriginalFileName matches and the PE's FileVersion is at or above a minimum. The tightest commonly used rule level; brittle across self-updating applications whose binaries change without warning [33][43].
A worked example
Generating a FilePublisher rule for a Microsoft-signed binary on PowerShell:
New-CIPolicyRule -FilePath "C:\Path\To\App.exe" -Level FilePublisher
produces a <FileRule> whose XML carries the issuing CA, the leaf-cert subject CN, the OriginalFileName from the version resource, and a MinimumFileVersion attribute. Every one of those fields is a direct read of the Authenticode SignerInfo and the PE version resource; nothing in the rule generation step talks to Microsoft. The administrator owns the rule.
WDAC's vocabulary makes one structural choice explicit that the article has been implicit about until now: trust is administrator-authored, not vendor-authored. The cryptographic identity is supplied by the same Authenticode primitives we just dissected; the policy is whatever the organisation writes. Before we look at the limits of what this stack can prove, one quick detour into how other operating systems have approached the same problem.
8. Catalog-vs-embedded across operating systems
Windows is unusual in two specific ways: it stores the catalog on the endpoint, and it refreshes the catalog through the OS update channel. No other mainstream OS does both.
| System | Signature carrier | Catalog model? | Transparency log? | Counter-signing for longevity |
|---|---|---|---|---|
| Windows (Authenticode) | PKCS#7 / CMS SignedData inside WIN_CERTIFICATE | Yes -- .cat files in CatRoot, refreshed by Windows Update [17] | No | Yes -- RFC 3161 token as unsigned attribute [41] |
| macOS | Apple-issued code signature + Notarization ticket; ticket stapled to artefact or fetched online [44] | No -- Notarization ticket attests, but there is no on-disk "list of hashes" structure | No | Stapled ticket effectively gives a signing-time guarantee; no third-party TSA |
| Linux IMA / EVM | Extended-attribute signatures on individual files [45] | No -- per-file security.ima xattr | No | Out of scope; appraised against locally trusted keyring |
| Android | APK Signature Scheme v3 (block inside the APK) [46] | No | No (signatures live inside the APK) | Proof-of-rotation chain inside the v3 block lets a publisher rotate keys without re-signing |
| sigstore (OCI artefacts) | Detached signature in OCI registry; short-lived Fulcio cert [47] | Closest analogue -- detached signature can cover blobs [48] | Yes -- Rekor [49] | TSA-style entries possible via Rekor |
The closest design analogue to the Windows catalog model is sigstore. Both decouple the signature from the artefact, and both let a single signing event cover many files. The difference is where the detached signature lives. Windows puts the .cat on the endpoint and refreshes the catalog through the OS update channel; sigstore stores the detached signature in an OCI registry and writes an attestation to a Rekor transparency log. That difference is also what gives Windows the offline-stale-catalog problem (a disconnected endpoint cannot freshness-check CatRoot) and gives sigstore the offline-no-Rekor problem (a disconnected verifier cannot consult the log). Readers who want the broader cross-platform identity comparison should consult the earlier App Identity in Windows article in this series, which compares Apple's package identity, Android's app IDs, and Linux's lack of a unified equivalent in more depth. The present article only summarises the signature-carrier side of the comparison.
Whether Microsoft puts the catalog on the endpoint or in an OCI registry is a deployment choice. The limit of what any signature -- catalog, embedded, sigstore-anchored, Apple-notarised -- can prove is a deeper, more uncomfortable claim. We turn to that next.
9. What signatures cannot prove
Stuxnet did not break Authenticode. It walked through it. The same is true of Flame, of ShadowHammer, and of the Bitwarden CLI npm hijack. Every named incident on the modern Windows code-signing timeline is an instance of the same structural lower bound: signatures prove who, not what. The Windows code-identity stack has spent fourteen years adding layers that narrow the consequences of that bound. None of them eliminate it.
Four limits are worth naming explicitly.
L1. Provenance is not safety
By Rice's theorem corollary, no decision procedure can determine arbitrary non-trivial semantic properties of a program. A signing system can therefore certify only "this binary came from a key-holder," never "this binary is benign." Stuxnet 2010 [2], Flame 2012 [22][23], Operation ShadowHammer 2019 [36], and the Bitwarden CLI npm hijack of 22 April 2026 [37][39][38] are four independent instances of the same gap, across four entirely different attack surfaces (stolen kernel-driver key; forged sub-CA via MD5 collision; compromised ASUS Live Update certificate; compromised npm OIDC trusted-publishing). The empirical scale is large: Kim, Kwon, and Dumitraș measured millions of certificates and hundreds of thousands of signed-but-malicious PE samples in the Windows code-signing PKI in their CCS 2017 paper [50].
The mathematics of Rice's theorem is succinct. Let be any non-trivial semantic property of programs (e.g. is malicious). For any algorithm that on input program outputs claiming whether has property , there exists a program where is wrong. A signature scheme is not such an algorithm in the first place: it computes . The signature output has no semantic content about 's behaviour; it asserts only that the holder of touched .
L2. CA cardinality and the weakest-link property
The trust graph for kernel-mode loads is narrow: a small number of Microsoft roots [7]. The trust graph for user-mode loads is the union of every root in the system Trusted Root store -- a much larger set. Any one root, if compromised, degrades the entire user-mode code-identity trust graph; any one sub-CA, if forged, opens the kernel-mode path for the lifetime of the certificate. The Sotirov / Stevens / Appelbaum / Lenstra / Molnar / Osvik / de Weger rogue-CA work from December 2008 [24] demonstrated this dynamic for the web PKI; the same family of attack was then mounted in Flame in 2012 against the Microsoft Enforced Licensing Intermediate PCA [23]. The CSBR's EV-on-hardware requirements [27] reduce stolen-key risk at the leaf level, but a forged sub-CA bypasses the leaf entirely.
L3. Catalog-store freshness on disconnected endpoints
A disconnected endpoint cannot freshness-check its CatRoot. The catalog database is whatever Windows Update last delivered -- which means freshly issued catalogs covering newly shipped inbox files cannot be trusted on machines that have been offline. The Vulnerable Driver Blocklist faces the same problem in reverse: a freshly blocked driver does not become un-trusted on a disconnected endpoint until the supplemental policy lands. Microsoft acknowledges this in the VDB documentation: "It's often necessary for us to hold back some blocks to avoid breaking existing functionality" [34]. The publication lag is deliberate, not accidental, and there is no in-band way for an endpoint to ask "is my VDB current?"
L4. TSA centralisation and antedating
RFC 3161 has no transparency log. A compromised TSA can issue countersignatures with arbitrary genTime undetectably, until and unless the TSA's root is revoked. Sigstore Rekor [49] is the canonical answer to this problem in the OSS world; nothing equivalent ships in the Authenticode stack. The consequence is asymmetric: a compromised TSA can antedate a signature backwards, making a freshly signed but recently malicious binary appear to have been signed before the malicious campaign began -- which on most verifiers means it will still verify even after the actual signing certificate is revoked.
Diagram source
flowchart TD
L1["L1: Provenance != safety
(Rice's theorem corollary)"]
L2["L2: CA cardinality
(weakest-link property)"]
L3["L3: CatRoot freshness
(offline endpoints stale)"]
L4["L4: TSA centralisation
(no transparency log)"]
Floor["What is actually being proved:
a key-holder touched hash(p) at genTime"]
L1 --> Floor
L2 --> Floor
L3 --> Floor
L4 --> Floor A valid signature proves only who signed the binary, never what the binary does.
Authenticode is the floor of Windows trust, not the ceiling. Every later layer -- Kernel-Mode Code Signing, App Control for Business, the Vulnerable Driver Blocklist, HVCI page-hash enforcement -- exists because the floor cannot, by construction, do more.
Once you see provenance and safety as separate questions, every open problem in the code-signing stack lines up in one direction: how do you reduce the blast radius of the inevitable next valid-but-malicious signature?
10. Open problems
Five problems are concrete enough to call out as ongoing work.
O1. Post-quantum Authenticode. Microsoft has not yet published a SpcIndirectDataContent variant that references the ML-DSA (FIPS 204 [51]) or SLH-DSA (FIPS 205 [52]) OIDs. The CA/B Forum CSBR has not named a post-quantum algorithm for code-signing certificates; the current CSBR v3.8 [27] still rests on RSA and ECDSA. NIST's PQC programme has set a 2035 deadline to deprecate quantum-vulnerable algorithms [53]. The CMS extensibility precedents are there: RFC 8554 profiles stateful LMS [54], RFC 8419 profiles EdDSA in CMS [55], and there is no architectural reason the same approach cannot profile ML-DSA. A hybrid-signed binary that carries both an RSA and an ML-DSA SignerInfo inside the same SignedData is technically possible today, and Microsoft will likely have to ship it before catastrophic loss of confidence in RSA can happen. FIPS 204 (ML-DSA) and FIPS 205 (SLH-DSA) were both finalised on 13 August 2024 [51][52]. The standards are stable; what is missing is the Authenticode-side OID registration and the Hardware Developer Center portal-signing pipeline that would emit a PQ counter-signature. The CSBR side and the Microsoft side both have to move; neither has publicly committed to a date.
O2. Per-page integrity for non-PE artefacts. Page hashes inside SpcPeImagePageHashes2 [12] are PE-specific. PowerShell scripts, MSIX packages, Appx packages, and the .cat files themselves rely on whole-file Authenticode hashing; if an attacker can corrupt a single byte after load, the OS does not currently re-hash. HVCI gives PE binaries a runtime check; the script and package side does not yet have an equivalent.
O3. Transparency logs for Authenticode countersignatures. RFC 3161 TSAs do not publish their issued tokens. A backdated countersignature from a compromised TSA is currently undetectable beyond CA revocation. Sigstore Rekor [49] demonstrates that a transparency log integrates with a signing pipeline at low overhead; there is no equivalent for the Microsoft-signed-driver world or for third-party Authenticode signers.
O4. Revocation propagation latency. The gap between "the CA revokes" and "every endpoint refuses to verify" is empirically days to weeks. CRLs are downloaded on a cadence (with EnableCertPaddingCheck aside, OCSP is not even applied to Authenticode by default). The VDB's quarterly cadence [34] is faster than CRL-only and slower than the rate at which attackers can stand up an attack with a freshly stolen certificate. Some of this is unavoidable -- you cannot push a revocation faster than an offline endpoint can reach Windows Update -- but a structurally better answer is one of the open questions.
O5. Post-CrowdStrike (July 2024) kernel-driver-loading discipline. Microsoft's Windows Resiliency Initiative was announced in the wake of the 19 July 2024 CrowdStrike Falcon Sensor outage; a fully-specified replacement for today's third-party kernel-driver model has not yet shipped. A successful answer would push parts of today's Authenticode + KMCS + WDAC story toward sandboxed user-mode driver frameworks, with the kernel restricted to a much narrower interface. The Authenticode primitives this article has dissected will still be the substrate; what gets layered on top is the open architectural question.
The next decade of Windows code-signing is going to be dominated by post-quantum migration and by whatever the Windows Resiliency Initiative converges to. Both will be evolution, not revolution: they will sit on top of the certificate-table, catalog-store, and timestamp-token primitives that have been load-bearing since 1996. To finish, the day-to-day commands that interrogate every byte we have discussed.
11. Practical guide: signtool, certutil, New-CIPolicyRule
If you have read this far, you should be able to run the following commands on a Windows host and explain every field of their output. Microsoft's signtool, certutil, and the ConfigCI PowerShell module are the canonical tools [56].
Verify a signed binary end to end
signtool verify /v /pa /all "C:\Path\To\binary.exe"
The output prints, in order: the SHA-256 of the file's Authenticode hash, the leaf certificate's subject and issuer, every intermediate up to the trusted root, the RFC 3161 timestamp's genTime, and the policy used to validate. /pa opts into the "default authenticode" policy (instead of the deprecated MicrosoftRoot policy); /all walks every signature on the file rather than just the strongest.
Compute and look up an Authenticode hash
certutil -hashfile "C:\Path\To\driver.sys" SHA256
certutil -CatDB "C:\Windows\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}" /v /search <hash>
The -hashfile command emits the file SHA-256, which is not the Authenticode hash (the file SHA-256 includes the certificate-table bytes; the Authenticode hash excludes them). The Authenticode hash is what is stored inside each catalog's CatalogList. Get-AuthenticodeSignature is the easier PowerShell route to the Authenticode hash directly.
Walk the catalog store
Get-ChildItem "C:\Windows\System32\CatRoot" -Recurse | Select-Object FullName
The GUID-named subfolder is the CryptSvc policy database identifier; the .cat files inside are individually-signed SignedData blobs whose encapContentInfo is a CatalogList [17]. CatRoot2 holds staging copies and the catalog database index.
Generate a WDAC rule
New-CIPolicyRule -FilePath "C:\Path\To\App.exe" -Level FilePublisher
This produces an XML <FileRule> element with the issuer, subject CN, original file name, and minimum file version. Pipe the result into New-CIPolicy to build a policy XML; convert to binary with ConvertFrom-CIPolicy and deploy via Group Policy or Intune.
Decide between embedded and catalog signing
For an internal line-of-business app shipped as a single MSI, embedded signing is the default and the cleanest choice. For a multi-binary package where some files are third-party and unsignable, the Package Inspector workflow [40] builds a .cat covering the post-installation file set without modifying any binary:
PackageInspector.exe Start C:\
... install your app ...
PackageInspector.exe Stop C:\ -Name MyApp.cat -ResultsFile C:\Temp\MyApp_inspection.txt
Confirm a kernel-mode chain
signtool verify /v /pa /kp "C:\Windows\System32\drivers\example.sys"
The /kp policy uses the kernel-mode driver policy: the chain must terminate at a kernel-mode-trusted root (the Microsoft Code Verification Root family of anchors, or a portal-signed-driver Microsoft Root Authority anchor). certutil -store -enterprise root enumerates the local kernel-mode roots; the legacy Microsoft Code Verification Root is named on the KMCS policy page [7] but its thumbprint is not published on a stable Microsoft Learn URL -- you read it via certutil -store on the running system.
Make an informed EnableCertPaddingCheck decision
The strict-parser registry value lives in two places. Set both:
reg add "HKLM\Software\Microsoft\Cryptography\Wintrust\Config" /v EnableCertPaddingCheck /t REG_DWORD /d 1 /f
reg add "HKLM\Software\Wow6432Node\Microsoft\Cryptography\Wintrust\Config" /v EnableCertPaddingCheck /t REG_DWORD /d 1 /f
CISA added CVE-2013-3900 to the Known Exploited Vulnerabilities catalogue on 10 January 2022 [15]; treat this as effectively mandatory in any hardened-baseline build.
Annotated signtool verify output
Verifying: notepad.exe
Hash of file (sha256): 6B9B7E... <-- Authenticode hash, the same one
inside SpcIndirectDataContent.messageDigest
Signing Certificate Chain:
Issued to: Microsoft Root Certificate Authority 2010 <-- root anchor
Issued by: Microsoft Root Certificate Authority 2010
Issued to: Microsoft Windows Production PCA 2011 <-- intermediate / PCA
Issued by: Microsoft Root Certificate Authority 2010
Issued to: Microsoft Windows <-- leaf / signer
Issued by: Microsoft Windows Production PCA 2011
The signature is timestamped: Thu Jul ... <-- RFC 3161 genTime
Timestamp Verified by:
Issued to: Microsoft Time-Stamp PCA 2010 <-- TSA chain
Issued to: Microsoft Time-Stamp Service
File is signed and the signature was verified.
// Cross-platform pedagogy: this snippet shows the flow of a catalog lookup.
// On Windows, "certutil -CatDB <CatRootPath> /v /search <hash>" returns the
// covering catalog file. Off Windows, we mock the output so the flow is visible.
interface CatalogLookupResult {
hash: string;
catalogFile: string | null;
signerSubject: string | null;
}
function lookupCatalog(authenticodeHash: string): CatalogLookupResult {
// Real implementation would shell out to:
// certutil -CatDB <CatRoot GUID path> /v /search <authenticodeHash>
// Parse the output for "Hash: <hash> Catalog: <path>".
const known: Record<string, CatalogLookupResult> = {
"6B9B7E...": {
hash: "6B9B7E...",
catalogFile: "C:\\Windows\\System32\\CatRoot\\{F750E6C3-...}\\Package_for_KB12345.cat",
signerSubject: "CN=Microsoft Windows Production PCA 2011"
}
};
return known[authenticodeHash] || { hash: authenticodeHash, catalogFile: null, signerSubject: null };
}
const r = lookupCatalog("6B9B7E...");
console.log(r.catalogFile ? "Catalog-signed by " + r.signerSubject : "Not catalog-covered"); Press Run to execute.
Why your internally-signed LOB app trips SmartScreen
SmartScreen Application Reputation is not gated on Authenticode validity. It is gated on certificate class (EV vs. OV) and on aggregate download volume and reporting. An internally signed enterprise LOB app has neither: it is signed with an OV certificate, and its download volume is at most a few hundred enterprise users. The fix has two paths. The cheap one is to ride your enterprise WDAC policy rather than fight SmartScreen -- App Control rules allow the binary unconditionally inside your organisation. The expensive one is to buy an EV certificate, push the binary through a small early-access user pool, and let SmartScreen accumulate the reputation signal. Both work. Fighting SmartScreen with a louder OV signature does not.
These seven commands cover the full surface of what Authenticode, catalog signing, and WDAC let a Windows engineer actually inspect. Everything else in this article is context for what those command outputs mean.
12. Frequently asked questions
Frequently asked questions
What is the difference between an Authenticode signature and a generic 'code signature'?
Authenticode is a specific PKCS#7 / CMS profile for signing Windows portable executables, catalog files, and a small set of related artefacts. It is defined by Microsoft's Authenticode_PE.docx specification [12] and is characterised by a PE-specific Authenticode hash (with four exclusions), the SpcIndirectDataContent content type at OID 1.3.6.1.4.1.311.2.1.4, and the WIN_CERTIFICATE certificate-table wrapper. Other code-signing schemes -- JAR signing for Java, APK Signature Scheme v3 for Android [46], sigstore/cosign for OCI artefacts [47], Apple Notarization for macOS [44] -- are not Authenticode-compatible. They solve similar problems with different envelopes.
If a publisher's end-entity certificate expires, does my signed binary stop working?
Not if the signature was RFC 3161 timestamped at signing time. The TimeStampToken in the unsigned attributes pegs the signing event to a genTime from a Trusted Time-Stamping Authority [41]; later verifiers compare genTime to the signing certificate's validity window and honour the signature so long as genTime was inside that window. The signature will stop working on hash-only WDAC rules (which do not consult certificate expiry at all) and on the rare verifiers that enforce chain time at validation. Signing without /tr is the way to produce a signature that silently loses validity at end-entity-cert expiry; that is the single most common Authenticode mistake at signing time.
Can I still install an unsigned kernel driver on Windows 11?
Only by enabling Test Signing mode (which puts a watermark on the desktop and refuses to coexist with Secure Boot), or by booting with Driver Signature Enforcement disabled (which is a one-boot bypass), or by using a vulnerable signed driver to load your unsigned code (the entire point of the Vulnerable Driver Blocklist [34]). Production loading of an unsigned driver on a normally configured Windows 11 system is not supported. Cross-signing for new end-entity certs has been closed since the 29 July 2015 issuance cutoff [7]; cross-certificates expired by July 2021 [31].
Why does my company's internally signed app still trip SmartScreen?
See the §11 Spoiler "Why your internally-signed LOB app trips SmartScreen" for the detailed explanation of why SmartScreen Application Reputation weights certificate class (EV vs. OV) and download volume rather than Authenticode validity, and for the two production fixes (ride your enterprise App Control policy, or buy an EV certificate and let reputation accumulate). The one-line summary: Authenticode and SmartScreen are different decision systems that happen to read the same SignerInfo -- making your signature louder in Authenticode does not buy you reputation in SmartScreen.
What is the difference between Microsoft Code Verification Root and Microsoft Code Signing PCA?
The Microsoft Code Verification Root is the historical kernel-mode trust anchor whose intermediate cross-signed third-party kernel code-signing CAs for pre-July-2015 drivers [7]. It is named in the KMCS policy document; its thumbprint is not published on a stable Microsoft Learn URL, so practitioners read it via certutil -store on the running system. The Microsoft Code Signing PCA family of intermediates (and its newer cousins like Microsoft Windows Production PCA 2011) are user-mode signing chains used for Microsoft-internal binaries and most WHQL catalogs. Both feed into WinVerifyTrust; they differ in which downstream consumer treats them as authoritative -- the kernel for the former, user-mode trust decisions for the latter.
Is the Authenticode hash the same as SHA-256 of the file?
No. The Authenticode hash excludes four PE regions: the optional-header CheckSum (4 bytes), the IMAGE_DIRECTORY_ENTRY_SECURITY data-directory entry (8 bytes), the certificate-table bytes themselves, and the file-alignment padding after each section [12]. So (Get-AuthenticodeSignature notepad.exe).Hash returns a different value than certutil -hashfile notepad.exe SHA256. The Authenticode hash is what is stored inside SpcIndirectDataContent.messageDigest and what is matched against catalog memberHash entries; the file SHA-256 is useful for forensic identification but does not appear anywhere in the signature flow.
Why does WDAC distinguish Publisher, FilePublisher, and WHQLFilePublisher?
They differ in precision and in which Authenticode fields they consult [33]. Publisher allows anything signed by a given issuing CA + leaf-cert subject CN; broadest but loosest. FilePublisher adds OriginalFileName + MinimumFileVersion constraints; tightens to a specific binary at a min version. WHQLFilePublisher further requires the WHQL EKU; the strictest commonly used rule level. Self-updating apps invalidate FilePublisher rules silently when their OriginalFileName or FileVersion change without warning [43]; most enterprises start at Publisher and tighten only for high-risk binaries.
Did the CVE-2013-3900 fix ever ship as default-on?
No. NVD's verbatim Microsoft language: "Microsoft does not plan to enforce the stricter verification behavior as a default functionality on supported releases of Microsoft Windows. This behavior remains available as an opt-in feature via reg key setting, and is available on supported editions of Windows released since December 10, 2013" [15]. CISA added the CVE to the Known Exploited Vulnerabilities catalogue on 10 January 2022 with a federal due date of 10 July 2022. Hardened environments should set EnableCertPaddingCheck=1 in both the native and Wow6432Node registry paths.
13. Closing reflection
In August 1996 the Authenticode trust decision was a single yes/no answer to a single question: did this PKCS#7 SignedData blob, attached to this downloadable ActiveX control, validate against a CA in the user's browser? Thirty years later, the trust decision is a chained question composing every primitive in this article: a WIN_CERTIFICATE record points to a SignedData envelope; the envelope's SpcIndirectDataContent carries an Authenticode hash and optional page hashes; an unsigned attribute carries an RFC 3161 timestamp; the catalog store may carry a parallel signature for the same hash; the certificate chain terminates at one of a small set of Microsoft anchors for kernel-mode loads; an administrator's App Control policy decides whether the verdict survives the rule evaluation; the Vulnerable Driver Blocklist denies a small curated list outright.
The cryptography has not moved. The certificate table is still where the bytes live. PKCS#7 SignedData is still the envelope. RSA is still the signature algorithm. What has changed -- and what is going to keep changing through the post-quantum migration and whatever the Windows Resiliency Initiative converges to -- is the layering of policy on top.
Authenticode is not the ceiling. It is the floor. Everything else is built on top, and the next time a Realtek certificate is stolen, those layers are what decides whether the next Stuxnet still loads.
Study guide
Key terms
- Authenticode
- Microsoft's PKCS#7 / CMS profile for signing Windows PE binaries, defined by Authenticode_PE.docx.
- WIN_CERTIFICATE
- The PE certificate-table record (dwLength, wRevision, wCertificateType, bCertificate[]) wrapping the PKCS#7 SignedData blob.
- SpcIndirectDataContent
- Microsoft eContentType (OID 1.3.6.1.4.1.311.2.1.4) whose messageDigest is the Authenticode hash; signs a hash, not a file.
- Authenticode hash
- The PE digest computed with four regions excluded (CheckSum, SECURITY data-directory entry, certificate-table bytes, section-padding).
- Page hash (SpcPeImagePageHashes2)
- Signed attribute carrying per-4 KiB-page hashes for HVCI demand-fault-time verification.
- Catalog file (.cat)
- A degenerate SignedData whose encapsulated content is a CatalogList of (memberHash, attributes) tuples; detached signature.
- CatRoot / CryptSvc
- On-endpoint catalog store at %SystemRoot%\System32\CatRoot\{GUID}\ and the service that indexes member hashes.
- Trusted Time-Stamping Authority (TSA)
- RFC 3161 service that counter-signs a signature's hash with a trusted genTime, attached as an unsigned attribute.
- WinVerifyTrust
- CryptoAPI function orchestrating the Authenticode verification pipeline.
- Code Integrity / ci.dll
- Windows kernel-mode component enforcing KMCS on driver loads and feeding page hashes to HVCI.
- Microsoft Code Verification Root
- Historical kernel-mode trust anchor for cross-signed third-party drivers; thumbprint read via certutil -store.
- App Control for Business (WDAC)
- Post-2024 rename of Windows Defender Application Control; consumes Authenticode primitives as policy inputs.
- FilePublisher rule
- WDAC rule level allowing Publisher + OriginalFileName + MinimumFileVersion combinations.
- Vulnerable Driver Blocklist (VDB)
- Microsoft-curated supplemental deny policy enabled by default since Windows 11 22H2; quarterly cadence.
- RFC 3161 TimeStampToken
- CMS SignedData over hash(signature) || genTime, attached at OID 1.2.840.113549.1.9.16.2.14 as an unsigned attribute.
References
- Stuxnet. https://en.wikipedia.org/wiki/Stuxnet - First-uncovered date (17 June 2010) and the US-Israel "Operation Olympic Games" attribution. ↩
- (2011). W32.Stuxnet Dossier (Version 1.4). https://archive.org/download/w32_stuxnet_dossier/w32_stuxnet_dossier.pdf - Symantec Security Response report documenting the Realtek and JMicron signed driver components. ↩
- Internet Archive Item: W32.Stuxnet Dossier. https://archive.org/details/w32_stuxnet_dossier - Stable Internet Archive metadata page for the Symantec dossier. ↩
- Authenticode Signatures. https://learn.microsoft.com/en-us/windows-hardware/drivers/install/authenticode - Microsofts statement that embedded and catalog signatures coexist; catalog signing is a type of detached signature. ↩
- PE Format. https://learn.microsoft.com/en-us/windows/win32/debug/pe-format - Canonical PE/COFF format spec defining IMAGE_DIRECTORY_ENTRY_SECURITY. ↩
- App Control for Business. https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/ - Canonical hub for the 2024 WDAC -> App Control for Business rename. ↩
- Kernel-Mode Code Signing Policy (Windows Vista and Later). https://learn.microsoft.com/en-us/windows-hardware/drivers/install/kernel-mode-code-signing-policy--windows-vista-and-later- - The 29 July 2015 cross-signing cutoff and the Windows 10 1607+ KMCS root chain. ↩
- (1996). Microsoft and VeriSign Provide First Technology for Secure Downloading of Software Over the Internet. https://news.microsoft.com/source/1996/08/07/microsoft-and-verisign-provide-first-technology-for-secure-downloading-of-software-over-the-internet/ - Original 7 August 1996 Microsoft press release announcing Authenticode in Internet Explorer 3 beta 2. ↩
- Internet Explorer 3. https://en.wikipedia.org/wiki/Internet_Explorer_3 - IE 3 RTM date (13 August 1996); cross-check for the Authenticode debut window. ↩
- (1998). PKCS #7: Cryptographic Message Syntax Version 1.5 (RFC 2315). https://datatracker.ietf.org/doc/html/rfc2315 - The original IETF republication of the PKCS#7 envelope that wraps every Authenticode signature. ↩
- (2009). Cryptographic Message Syntax (CMS) (RFC 5652). https://datatracker.ietf.org/doc/html/rfc5652 - The modern CMS specification that supersedes PKCS#7 v1.5; Authenticode parsers consume the CMS dialect. ↩
- (2008). Windows Authenticode Portable Executable Signature Format. https://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx - Canonical Microsoft Authenticode hash specification; defines SpcIndirectDataContent, SpcPeImageData, SpcPeImagePageHashes2. ↩
- (1978). A Method for Obtaining Digital Signatures and Public-Key Cryptosystems. https://people.csail.mit.edu/rivest/Rsapaper.pdf - The signature scheme used by virtually every Authenticode SignerInfo on disk today. ↩
- (1976). New Directions in Cryptography. https://ee.stanford.edu/~hellman/publications/24.pdf - Public-key cryptography foundation, including the digital-signature concept that Authenticode later instantiates. ↩
- CVE-2013-3900 -- WinVerifyTrust Signature Verification. https://nvd.nist.gov/vuln/detail/CVE-2013-3900 - NVD entry preserving Microsofts opt-in policy and the CISA KEV addition (10 January 2022). ↩
- (2013). Microsoft Security Bulletin MS13-098. https://docs.microsoft.com/en-us/security-updates/securitybulletins/2013/ms13-098 - Original December 2013 patch for the CVE-2013-3900 WinVerifyTrust parser flaw. ↩
- Catalog Files and Digital Signatures. https://learn.microsoft.com/en-us/windows-hardware/drivers/install/catalog-files - The canonical Microsoft Learn page defining .cat files, the CatRoot directory, and the single-byte-invalidates rule. ↩
- Driver Signing. https://learn.microsoft.com/en-us/windows-hardware/drivers/install/digital-signatures - Microsofts canonical driver-package signing narrative. ↩
- Kernel Patch Protection. https://en.wikipedia.org/wiki/Kernel_Patch_Protection - PatchGuard introduction in 2005 alongside KMCS on x64 Windows. ↩
- Windows Vista. https://en.wikipedia.org/wiki/Windows_Vista - RTM date November 8, 2006 and general availability January 30, 2007; cross-check for KMCS debut. ↩
- Flame (malware). https://en.wikipedia.org/wiki/Flame_(malware) - 28 May 2012 disclosure; Microsoft sub-CA forgery via MD5 chosen-prefix collision. ↩
- (2013). Counter-Cryptanalysis. https://link.springer.com/chapter/10.1007/978-3-642-40041-4_8 - Forensic identification of the Flame collision technique (CRYPTO 2013). ↩
- (2012). Microsoft Security Advisory 2718704. https://docs.microsoft.com/en-us/security-updates/securityadvisories/2012/2718704 - June 2012 revocation of the Flame-forged Microsoft Enforced Licensing Intermediate PCA certificates. ↩
- (2008). MD5 considered harmful today: Creating a rogue CA certificate. https://www.win.tue.nl/hashclash/rogue-ca/ - The 25C3 (December 2008) disclosure of the rogue-CA chosen-prefix MD5 collision; cryptanalytic precedent for Flame. ↩
- Code signing. https://en.wikipedia.org/wiki/Code_signing - Secondary overview of code signing; redirect target for the Wikipedia Authenticode article. ↩
- (2016). Minimum Requirements for the Issuance and Management of Publicly-Trusted Code Signing Certificates. https://pkic.org/uploads/2016/09/Minimum-requirements-for-the-Issuance-and-Management-of-code-signing.pdf - Initial CSBR version approved 22 September 2016; effective 1 February 2017. ↩
- CA/Browser Forum Code Signing Documents. https://cabforum.org/working-groups/code-signing/documents/ - Current CSBR v3.8 (5 August 2024) and changelog. ↩
- CA/Browser Forum Code Signing Certificate Working Group. https://cabforum.org/working-groups/code-signing/ - CSCWG charter and member list. ↩
- Attestation Signing a Kernel Driver for Public Release. https://learn.microsoft.com/en-us/windows-hardware/drivers/dashboard/attestation-signing-a-kernel-driver-for-public-release - The EV-cert + Partner Center counter-signature workflow for kernel drivers. ↩
- Driver Signing Offerings. https://learn.microsoft.com/en-us/windows-hardware/drivers/dashboard/driver-signing-offerings - WHQL retail-publishable vs attestation-not-publishable-on-Windows-Update distinction. ↩
- Deprecation of Software Publisher Certificates and Commercial Release Certificates. https://learn.microsoft.com/en-us/windows-hardware/drivers/install/deprecation-of-software-publisher-certificates-and-commercial-release-certificates - Cross-signing fully closed for new signatures by July 2021. ↩
- App Control for Business and AppLocker Overview. https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/appcontrol-and-applocker-overview - Microsoft Learn overview documenting App Control introduction with Windows 10 (originally as configurable code integrity / Device Guard, 2015) and the 2017 WDAC naming. ↩
- Select Types of Rules to Create. https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/select-types-of-rules-to-create - The WDAC rule-options table and per-file rule levels (Hash, FileName, Publisher, FilePublisher, WHQL, etc.). ↩
- Microsoft Recommended Driver Block Rules. https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/microsoft-recommended-driver-block-rules - Vulnerable Driver Blocklist: default-on since Windows 11 2022 Update, quarterly cadence, deliberate publication lag. ↩
- (2021). Improve Kernel Security with the New Microsoft Vulnerable and Malicious Driver Reporting Center. https://www.microsoft.com/en-us/security/blog/2021/12/08/improve-kernel-security-with-the-new-microsoft-vulnerable-and-malicious-driver-reporting-center/ - December 2021 launch of the centralised vulnerable-driver reporting workflow that feeds the VDB. ↩
- (2019). Operation ShadowHammer. https://securelist.com/operation-shadowhammer/89992/ - Kaspersky analysis of the ASUS Live Update supply-chain compromise. ↩
- (2026). Bitwarden Statement on the Checkmarx Supply Chain Incident. https://community.bitwarden.com/t/bitwarden-statement-on-checkmarx-supply-chain-incident/96127 - Vendor incident statement for the 22 April 2026 @bitwarden/cli compromise. ↩
- (2026). Bitwarden CLI Compromised in Ongoing npm Supply-Chain Campaign. https://thehackernews.com/2026/04/bitwarden-cli-compromised-in-ongoing.html - Cross-confirmation reporting on the @bitwarden/cli@2026.4.0 hijack. ↩
- (2026). Bitwarden CLI Hijacked on npm: Bun-Staged Credential Stealer. https://www.stepsecurity.io/blog/bitwarden-cli-hijacked-on-npm-bun-staged-credential-stealer-targets-developers-github-actions-and-ai-tools - Technical analysis identifying the first npm OIDC Trusted-Publishing supply-chain hijack. ↩
- Deploy Catalog Files to Support App Control for Business. https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/deployment/deploy-catalog-files-to-support-appcontrol - PackageInspector.exe workflow for catalog-signing unsigned line-of-business apps. ↩
- (2001). Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP) (RFC 3161). https://datatracker.ietf.org/doc/html/rfc3161 - Defines the TimeStampToken attribute that keeps decades-old Authenticode signatures verifying after their end-entity certificates expire. ↩
- App Control for Business Event ID Explanations. https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/operations/event-id-explanations - Reference for WDAC event IDs 3076 (audit) and 3077 (enforcement). ↩
- Use Code Signing for Better Control and Protection. https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/deployment/use-code-signing-for-better-control-and-protection - Embedded-vs-catalog semantics, multi-signature files, self-updating-app caveat. ↩
- Protecting Against Malware in macOS. https://support.apple.com/guide/security/protecting-against-malware-sec469d47bd8/web - Apples Notarization and Gatekeeper documentation; comparison-class source. ↩
- Linux Integrity Subsystem. https://sourceforge.net/p/linux-ima/wiki/Home/ - Linux IMA/EVM project wiki; comparison class. ↩
- APK Signature Scheme v3. https://source.android.com/docs/security/features/apksigning/v3 - AOSP APK Signature Scheme v3, including proof-of-rotation. ↩
- Sigstore Overview. https://docs.sigstore.dev/about/overview/ - Project overview of the Fulcio + Rekor + cosign code-signing stack. ↩
- Signing Blobs with Cosign. https://docs.sigstore.dev/cosign/signing/signing_with_blobs/ - Detached-signature workflow analogous to .cat files. ↩
- sigstore/rekor (GitHub). https://github.com/sigstore/rekor - Reference implementation of the Rekor transparency log. ↩
- (2017). Certified Malware: Measuring Breaches of Trust in the Windows Code-Signing PKI. https://users.umiacs.umd.edu/~tdumitra/papers/CCS-2017.pdf - CCS 2017 empirical measurement of signed-but-malicious PE samples in the Windows PKI. ↩
- (2024). FIPS 204: Module-Lattice-Based Digital Signature Standard. https://csrc.nist.gov/pubs/fips/204/final - ML-DSA standard published 13 August 2024. ↩
- (2024). FIPS 205: Stateless Hash-Based Digital Signature Standard. https://csrc.nist.gov/pubs/fips/205/final - SLH-DSA standard published 13 August 2024. ↩
- NIST Post-Quantum Cryptography Project. https://csrc.nist.gov/projects/post-quantum-cryptography - NIST PQC programme; 2035 deprecation timeline for quantum-vulnerable algorithms. ↩
- (2019). Leighton-Micali Hash-Based Signatures (RFC 8554). https://datatracker.ietf.org/doc/html/rfc8554 - Stateful hash-based PQ-safe signature scheme; a candidate primitive for post-quantum Authenticode. ↩
- (2018). Use of Edwards-Curve Digital Signature Algorithm (EdDSA) Signatures in the Cryptographic Message Syntax (CMS) (RFC 8419). https://datatracker.ietf.org/doc/html/rfc8419 - Worked example of how new signature algorithms are profiled into CMS, relevant to future Authenticode PQ migration. ↩
- Cryptography Tools. https://learn.microsoft.com/en-us/windows/win32/seccrypto/cryptography-tools - Reference page covering signtool, makecat, certutil. ↩