<!-- https://pkizerotohero.com/handbook/code-signing/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> How code and document signing work: Authenticode, RFC 3161 timestamps, notarization, Sigstore, S/MIME and eIDAS, and why signed never means safe.

Source: https://pkizerotohero.com/handbook/code-signing/ · Last reviewed: 2026-10-20

Part VII: Beyond the browser · Chapter 24

# Signed and sealed: code and document signing

**In one sentence:**

A code or document signature proves who signed a file and that nobody changed it since; a trusted timestamp keeps that proof valid after the signing certificate expires.

## Who is this for

- [Developer](https://pkizerotohero.com/start/#developer)
- [IT ops](https://pkizerotohero.com/start/#itops)
- [Expert](https://pkizerotohero.com/start/#expert)
- [Leader](https://pkizerotohero.com/start/#leader)
- [Beginner](https://pkizerotohero.com/start/#beginner)

Level: **Practitioner**

- [New to this? Start with The idea](https://pkizerotohero.com/handbook/code-signing/#the-idea)
- [Developer? Jump to How it really works](https://pkizerotohero.com/handbook/code-signing/#how-it-really-works)
- [IT ops? At work](https://pkizerotohero.com/handbook/code-signing/#at-work)
- [Leader? Why it matters](https://pkizerotohero.com/handbook/code-signing/#why-it-matters-to-the-business)

## The idea

- Beginner

You download an installer, an app update or a driver. Two questions matter before you run it: who made it, and has anyone changed it on the way? [Code signing](https://pkizerotohero.com/handbook/glossary/#code-signing) answers both.

The publisher computes a [hash](https://pkizerotohero.com/handbook/glossary/#hash) of the file and makes a [digital signature](https://pkizerotohero.com/handbook/glossary/#digital-signature) over that hash with its [private key](https://pkizerotohero.com/handbook/glossary/#private-key). It ships the signature together with its certificate. Your computer recomputes the hash, checks the signature with the public key in the certificate, and checks that a trusted [certificate authority](https://pkizerotohero.com/handbook/glossary/#certificate-authority) issued that certificate to the publisher named in it. If one byte of the file changed, the hash no longer matches and the check fails.

So a valid signature tells you two things, and only two:

- **who signed it**: the name the CA checked before issuing the certificate;
- **that it is unchanged**: the file is exactly what that signer signed.

It does not tell you the software is safe, honest or free of bugs. A criminal can register a company, buy a code signing certificate in its name and sign malware. Stolen signing keys and hacked build systems have also been used to sign malware with real companies’ names. **Signed is not the same as safe.** A signature gives you a name to hold responsible and a guarantee that nobody tampered with the file after signing. Deciding whether to trust that name is still your job, or your security tools’ job.

There is one more problem to solve. Certificates expire, and code signing certificates now live for little more than a year. Software lives much longer. If every signature died with its certificate, a perfectly good installer would suddenly look broken. The fix is a [timestamp](https://pkizerotohero.com/handbook/glossary/#timestamp): right after signing, the publisher asks a [timestamping authority](https://pkizerotohero.com/handbook/glossary/#timestamping-authority) (TSA) to countersign the signature with the current time. Later, a verifier asks: “was the certificate valid at the time in the timestamp?” If yes, the signature stays valid long after the certificate itself has expired.

The same ideas protect documents and email. A signed PDF contract, a signed email (S/MIME) and an EU qualified electronic signature all use a private key, a certificate and, for anything meant to last, a timestamp.

In the game

The Printing Press

In the game, Rosa runs the Printing Press. Every proclamation leaves with her signature, and the Royal Clock stamps the hour it was signed. Years later, after Rosa’s certificate has expired, Ken can still prove the proclamation is hers and unchanged, because the clock’s stamp shows it was signed while her certificate was valid. Then Mallory prints a pamphlet that is perfectly signed with her own, genuine certificate. The signature checks out. The pamphlet is still poison.

Where the analogy stops being true: a wax seal looks the same on every page, but a digital signature is different for every file, because it is computed from that file’s hash. And the time on a real timestamp is not something the signer writes. A separate authority signs it, so a signer cannot simply backdate their work.

## How it really works

- Developer
- Expert

### What exactly is signed, and where does the signature go?

Most code and document signatures use the same container: CMS `SignedData` ([RFC 5652, section 5](https://www.rfc-editor.org/rfc/rfc5652#section-5)), the format once called PKCS #7. It holds the hash algorithm, the signature value, information about the signer and usually the signer’s certificate chain. Where the container lives depends on the platform:

- **Embedded.** Windows Authenticode puts the signature inside the executable. The hash covers the file but skips the few bytes where the signature itself will be stored, so adding the signature does not change the hash.
- **Detached.** For archives and container images, the signature sits next to the file (`app.tar.gz.sig`, `app.p7s`) or in a registry, and the verifier needs both.
- **Inside the document.** A signed PDF stores the CMS container in a reserved area of the file, together with the byte ranges it covers.

The signing certificate must be meant for the job. A code signing certificate carries the `id-kp-codeSigning` purpose in its [extended key usage](https://pkizerotohero.com/handbook/glossary/#extended-key-usage) ([RFC 5280, section 4.2.1.12](https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12)). A TLS server certificate does not, and verifiers reject it for code.

### How does a timestamp keep a signature alive?

A trusted timestamp follows [RFC 3161](https://www.rfc-editor.org/rfc/rfc3161):

1. **The request.** The signer hashes its signature value and sends that hash (the “message imprint”), the hash algorithm and a random nonce to a TSA ([section 2.4.1](https://www.rfc-editor.org/rfc/rfc3161#section-2.4.1)). The TSA never sees the file itself.
2. **The token.** The TSA returns a time-stamp token: a CMS `SignedData` over a `TSTInfo` structure with the imprint, the time (`genTime`), a serial number, the TSA’s policy and the nonce ([section 2.4.2](https://www.rfc-editor.org/rfc/rfc3161#section-2.4.2)).
3. **The TSA’s own certificate.** It must carry exactly one extended key usage, `id-kp-timeStamping`, marked critical ([section 2.3](https://www.rfc-editor.org/rfc/rfc3161#section-2.3)). A key reserved for timestamps cannot be used to sign anything else.
4. **Attaching it.** The signer stores the token next to its signature, as an unsigned attribute. The token signs the signature, not the other way round, so nothing the publisher signed changes.

When a verifier later checks the file, it validates the TSA’s signature and chain, confirms the imprint matches, and then asks whether the signer’s certificate was valid **at the time in the token**, not today. TSA certificates are themselves valid for years (up to 135 months under the CA/Browser Forum rules), so timestamps stay checkable long after the signing certificate is gone.

*Diagram: A timeline. The signing certificate is valid from its start date to its expiry date. A file is signed inside that window and timestamped at the same moment. Years later, after the certificate has expired, the file is verified twice. With a timestamp, the verifier checks validity at the signing time and accepts it, marked with a tick. Without a timestamp, it checks validity today and rejects it as expired, marked with a cross.*

A trusted timestamp moves the question from 'is the certificate valid now?' to 'was it valid when this was signed?'.

Revocation uses the same clock. If a signing key is stolen, the CA revokes the certificate and, under the [Code Signing Baseline Requirements](https://cabforum.org/working-groups/code-signing/requirements/) (sections 4.9.1 and 4.9.6), may set the revocation date back to when the compromise began. Verifiers keep accepting signatures timestamped before that date and reject everything after it. Good, old releases survive. Malware signed with the stolen key does not.

### What rules apply to public code signing certificates?

Operating systems decide which CAs they trust for code signing. The CAs they trust follow the CA/Browser Forum’s Code Signing Baseline Requirements, which require the CA to verify the organisation (or individual) behind each certificate and set limits such as these:

- **1 June 2023:** the private keys of publicly trusted code signing certificates must be generated, stored and used in a hardware crypto module.
- **1 March 2026:** the maximum validity of a code signing certificate became 460 days.

The hardware rule changed build pipelines everywhere. A signing key may no longer sit in a file on a build server. It lives in a USB token, an on-premises [HSM](https://pkizerotohero.com/handbook/glossary/#hsm) or a cloud signing service, and the build calls out to it. That is more work, and it is the point: copying a key file is easy, while an HSM is built so that the key never leaves it.

### How do Windows, macOS and open-source projects check signatures?

- **Windows** verifies Authenticode signatures when you run downloaded programs and install drivers. [SignTool](https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool) signs and verifies them, and Microsoft Defender SmartScreen also weighs the reputation of the file and its publisher. A valid signature with no reputation can still trigger a warning.
- **macOS** expects software distributed outside the App Store to be signed with a Developer ID certificate that Apple issues, and to be [notarized](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution): Apple’s automated service scans the software for malicious components and issues a ticket that can be stapled to it. Gatekeeper checks the signature and the ticket before first launch. Here Apple is the CA, so it can revoke a Developer ID it no longer trusts.
- **Linux distributions** sign their package repositories with OpenPGP keys shipped with the system. It is not X.509, but the idea is the same: a trusted key, a signature, a hash.
- **Open-source projects and containers** increasingly use [Sigstore](https://docs.sigstore.dev/about/overview/). Instead of keeping a long-lived key, the signer proves an identity, such as an email account or a CI workflow, through OpenID Connect. Sigstore’s CA, Fulcio, issues a certificate for a fresh key that is valid for only 10 minutes. The signature and certificate are recorded in Rekor, a public transparency log, with a signed timestamp. Verifiers check the identity and its issuer, the log entry and that the signature was logged while the certificate was valid. It is [Certificate Transparency](https://pkizerotohero.com/handbook/certificate-transparency/) applied to software releases.

### What about email and documents?

**Email.** S/MIME ([RFC 8551](https://www.rfc-editor.org/rfc/rfc8551)) signs and encrypts email with CMS. The certificate names an email address in the subject alternative name and carries the email protection purpose. Signing and encrypting are separate: a signature proves the sender, and encryption needs the *recipient’s* certificate. Publicly trusted S/MIME certificates have their own rulebook: the CA/Browser Forum S/MIME Baseline Requirements apply to every publicly trusted S/MIME certificate (since 1 September 2023).

**Documents and EU law.** The EU’s eIDAS Regulation ([Regulation (EU) No 910/2014](https://eur-lex.europa.eu/eli/reg/2014/910/oj)) defines three levels of electronic signature:

| Level | What it requires |
| --- | --- |
| Electronic signature | Data in electronic form, attached to or logically associated with other data, that the signer uses to sign |
| Advanced electronic signature | Uniquely linked to and capable of identifying the signer, created with data the signer can use under their sole control, and any later change to the data is detectable (Article 26) |
| [Qualified electronic signature](https://pkizerotohero.com/handbook/glossary/#qualified-electronic-signature) | An advanced signature made with a qualified signature creation device and based on a qualified certificate from a qualified trust service provider (Article 3(12)) |

Only the last level carries an explicit guarantee in the regulation: a qualified electronic signature shall have the equivalent legal effect of a handwritten signature. eIDAS also defines electronic seals, the equivalent for organisations, and qualified electronic time stamps, which carry a legal presumption that their date and time are accurate (Article 41). Regulation (EU) 2024/1183 (eIDAS 2.0), in force since 20 May 2024, updates eIDAS and adds the European Digital Identity Wallet. Signed PDFs meant to last for decades embed timestamps and revocation data inside the file, so they can still be validated after the CA’s services are gone.

*Diagram: Left to right: the publisher's private key, kept in an HSM, signs the hash of the release. A timestamping authority adds a signed time. On the user's computer the verifier checks the certificate chain to a trusted root and the timestamp. Three results follow: who signed it, marked with a tick; unchanged since signing, marked with a tick; safe to run, marked with a cross and the words not proven.*

A signature proves who signed and that nothing changed. It never proves the software is safe.

## At work

- Developer
- IT ops

### How do I sign and verify a release file with OpenSSL?

A detached signature is the simplest form. The commands below use a demo code signing certificate (`cs.crt`) whose key is in a file, which is fine for learning. For public releases the key must stay in hardware.

```bash
openssl dgst -sha256 -sign cs.key -out app-1.4.2.tar.gz.sig app-1.4.2.tar.gz
openssl x509 -in cs.crt -noout -pubkey > cs-pub.pem
openssl dgst -sha256 -verify cs-pub.pem -signature app-1.4.2.tar.gz.sig app-1.4.2.tar.gz
```

```text
Verified OK
```

Change one byte of the file and the same check prints `Verification failure`. That proves integrity against the key, but not yet who the key belongs to. For that, use a CMS signature, which carries the certificate, and verify the chain and the purpose too:

```bash
openssl cms -sign -binary -in app-1.4.2.tar.gz -signer cs.crt -inkey cs.key -outform DER -out app.p7s
openssl cms -verify -binary -inform DER -in app.p7s -content app-1.4.2.tar.gz \
  -CAfile cs-ca.crt -purpose codesign -out verified.out
```

```text
CMS Verification successful
```

Leave out `-purpose codesign` and OpenSSL checks for the email-signing purpose by default, so the same signature fails with `Verify error: unsuitable certificate purpose`. The purpose check is doing its job.

### How do I get a trusted timestamp?

Any RFC 3161 client can talk to a TSA. With OpenSSL and curl, against the DigiCert TSA used in Microsoft’s SignTool examples:

```bash
openssl ts -query -data app-1.4.2.tar.gz -sha256 -cert -out app.tsq
curl -sS -H "Content-Type: application/timestamp-query" \
  --data-binary @app.tsq http://timestamp.digicert.com -o app.tsr
openssl ts -reply -in app.tsr -text
```

```text
Status info:
Status: Granted.
...
TST info:
Version: 1
Policy OID: 2.16.840.1.114412.7.1
Hash Algorithm: sha256
...
Time stamp: Oct 19 22:14:36 2026 GMT
Accuracy: unspecified
Ordering: no
Nonce: 0xFC70CB1C9E64DAC1
```

The TSA URL is plain HTTP on purpose: the token is signed, so the transport does not need to be trusted. To verify the token, extract the TSA’s certificates and check them against a bundle of trusted roots (`ca-bundle.crt` here):

```bash
openssl ts -reply -in app.tsr -token_out -out app.tst
openssl pkcs7 -inform DER -in app.tst -print_certs -out tsa-certs.pem
openssl ts -verify -data app-1.4.2.tar.gz -in app.tsr -CAfile ca-bundle.crt -untrusted tsa-certs.pem
```

```text
Verification: OK
```

Run the same verification against a changed file and it fails with `message imprint mismatch`.

### How do I check a signed program on Windows?

PowerShell reads Authenticode signatures directly. This program’s signing certificate expired months before the check, yet the signature is still valid because it was timestamped:

```powershell
$s = Get-AuthenticodeSignature "C:\Program Files\Git\usr\bin\bash.exe"
$s.Status
$s.SignerCertificate.NotAfter.ToString("yyyy-MM-dd")
$s.TimeStamperCertificate.Subject
```

```text
Valid
2026-05-06
CN=Sectigo Public Time Stamping Signer R35, O=Sectigo Limited, S=Manchester, C=GB
```

To sign, SignTool from the Windows SDK takes the file digest (`/fd`), an RFC 3161 TSA (`/tr`) and its digest (`/td`), as in [Microsoft’s documentation](https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool):

```powershell
signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /n "Example Corp" app.exe
signtool verify /pa /v app.exe
```

`/n` picks the certificate from the Windows certificate store by subject name. The private key itself stays in the token or HSM that the certificate store points to.

### How does keyless signing with Sigstore look?

With the `cosign` tool, signing opens an OpenID Connect login (or uses the CI workflow’s identity) and writes a bundle with the signature, certificate and log proof. Verification names the identity and issuer you expect, as in the [Sigstore documentation](https://docs.sigstore.dev/cosign/verifying/verify/):

```bash
cosign sign-blob app-1.4.2.tar.gz --bundle app.sigstore.json
cosign verify-blob app-1.4.2.tar.gz --bundle app.sigstore.json \
  --certificate-identity=release@example.com \
  --certificate-oidc-issuer=https://accounts.example.com
```

Checking the identity is the important part. A signature from *someone* proves little. A signature from the identity that should have signed proves a lot.

### What usually goes wrong?

- **No timestamp.** Every signature quietly expires with its certificate, now after little more than a year. Make the build fail if timestamping fails, instead of shipping without one.
- **Signing keys on build servers.** Public code signing keys must live in hardware. Also limit *who and what* can ask the HSM to sign: a compromised pipeline with signing rights can sign anything.
- **Signing everything at the end.** Sign what you built and tested, and verify the signature in the release step and on install.
- **Trusting “signed” in policy.** Allow-lists should name the publishers and identities you trust, not “any valid signature”.

Paste a code signing certificate into the decoder and check that its Extended Key Usage says Code Signing:

*Interactive tool: Certificate decoder (runs in the browser at [/tools/decoder/](https://pkizerotohero.com/tools/decoder/))*

## Common mistakes

- **“Signed software is safe software.”** A signature proves who signed and that nothing changed. Attackers can sign malware with their own certificates or with stolen keys.
- **“When the certificate expires, all my signed releases stop working.”** Not if they were timestamped. The verifier checks the certificate at the time in the timestamp.
- **“Signing encrypts the file.”** The file stays readable by anyone. A signature is computed over a hash with the private key and checked with the public key; it is not “encryption with the private key”.
- **“Signing an email also encrypts it.”** In S/MIME they are separate. Encryption needs the recipient’s certificate.
- **“A scanned handwritten signature is a qualified electronic signature.”** Under eIDAS, a qualified signature needs a qualified certificate and a qualified signature creation device.
- **“A TLS certificate can sign code too.”** Its extended key usage does not include code signing, so verifiers reject it.

## Why it matters to the business

- Leader

Why it matters to the business

Your signature is your brand on every release

Code signing puts your company’s name on every file you ship, and customers’ systems check it automatically. If the signing key leaks, attackers can ship malware under your name, and revoking the certificate can disrupt your own releases. Treat signing as a controlled process: keys in hardware (required for public code signing certificates), a small list of people and pipelines allowed to sign, and timestamps on every release so they outlive the certificate. For contracts and approvals in the EU, decide which processes need qualified electronic signatures and which only need advanced ones.

## Check yourself

Check yourself

A release was signed and timestamped two years ago. Its signing certificate expired last month. What does a verifier conclude?

1. The signature is invalid because the certificate expired
2. The signature is valid if the certificate was valid at the timestamp's time
3. The signature is valid only until the timestamp authority's certificate expires next week

Show answer

Answer B The signature is valid if the certificate was valid at the timestamp's time

What happened

The verifier checks the signing certificate against the time in the timestamp, not today's date.

Why

The TSA's signed time proves the signature existed while the certificate was valid.

The rule

Timestamp every signature that must outlive its certificate.

Check yourself

An app is validly signed with a genuine code signing certificate issued to an unknown company. What does that prove?

1. The app is safe to run
2. The app was signed by the company named in the certificate and has not changed since
3. Microsoft or Apple reviewed the app's behaviour

Show answer

Answer B The app was signed by the company named in the certificate and has not changed since

What happened

A valid signature proves the signer's identity, as the CA checked it, and the file's integrity.

Why

CAs verify who a publisher is, not what its software does. Malware can be signed too.

The rule

Signed is not safe. Decide whether you trust the signer.

Check yourself

Which electronic signature has the same legal effect as a handwritten signature throughout the EU?

1. Any electronic signature, such as a typed name
2. An advanced electronic signature made with software keys
3. A qualified electronic signature

Show answer

Answer C A qualified electronic signature

What happened

Only a qualified electronic signature has that guaranteed effect under eIDAS.

Why

It needs a qualified certificate from a qualified trust service provider and a qualified signature creation device.

The rule

Match the signature level to the legal weight the process needs.

## Go deeper

- [RFC 3161](https://www.rfc-editor.org/rfc/rfc3161): the Time-Stamp Protocol, the token format and the TSA certificate rules.
- [RFC 5652](https://www.rfc-editor.org/rfc/rfc5652): Cryptographic Message Syntax, the container behind Authenticode, S/MIME and PDF signatures.
- [CA/Browser Forum Code Signing Baseline Requirements](https://cabforum.org/working-groups/code-signing/requirements/): identity checks, key protection, validity limits and timestamp authorities.
- [Microsoft SignTool](https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool): every sign, timestamp and verify option.
- [Apple: Notarizing macOS software before distribution](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution): Developer ID, notarization and stapling.
- [Sigstore overview](https://docs.sigstore.dev/about/overview/): keyless signing with Fulcio and the Rekor transparency log.
- [RFC 8551](https://www.rfc-editor.org/rfc/rfc8551) and the [CA/Browser Forum S/MIME Baseline Requirements](https://cabforum.org/working-groups/smime/requirements/): signed and encrypted email.
- [eIDAS Regulation (EU) No 910/2014](https://eur-lex.europa.eu/eli/reg/2014/910/oj) and [Regulation (EU) 2024/1183](https://eur-lex.europa.eu/eli/reg/2024/1183/oj): electronic signatures, seals and time stamps in EU law.

The crash course covers this chapter in [lesson 10, Beyond the browser, and what comes next](https://pkizerotohero.com/course/beyond-the-browser/).

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/handbook/code-signing/).

Last reviewed 20 October 2026Edition 0.9

## Keep going

Crash course: [Lesson 10: Beyond the browser, and what comes next](https://pkizerotohero.com/course/beyond-the-browser/)

### How-to guides

- [How to sign code with signtool and cosign](https://pkizerotohero.com/guides/code-signing-signtool-cosign/)

### Tools

- [Certificate decoder](https://pkizerotohero.com/tools/decoder/): Paste a PEM certificate and read every field in plain words.

### Topics

- [Hashes and signatures](https://pkizerotohero.com/topics/signing/)
