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
Level: Practitioner
On this page
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 answers both.
The publisher computes a hash of the file and makes a digital signature over that hash with its 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 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: right after signing, the publisher asks a 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.
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), 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 (RFC 5280, 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:
- 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). The TSA never sees the file itself.
- The token. The TSA returns a time-stamp token: a CMS
SignedDataover aTSTInfostructure with the imprint, the time (genTime), a serial number, the TSA’s policy and the nonce (section 2.4.2). - The TSA’s own certificate. It must carry exactly one extended key usage,
id-kp-timeStamping, marked critical (section 2.3). A key reserved for timestamps cannot be used to sign anything else. - 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.
Revocation uses the same clock. If a signing key is stolen, the CA revokes the certificate and, under the Code Signing Baseline 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 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 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: 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. 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 applied to software releases.
What about email and documents?
Email. S/MIME (RFC 8551) 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) 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 | 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.
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.
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
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:
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
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:
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
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):
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
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:
$s = Get-AuthenticodeSignature "C:\Program Files\Git\usr\bin\bash.exe"
$s.Status
$s.SignerCertificate.NotAfter.ToString("yyyy-MM-dd")
$s.TimeStamperCertificate.Subject
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:
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:
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:
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
Check yourself
A release was signed and timestamped two years ago. Its signing certificate expired last month. What does a verifier conclude?
RightNot quite The answer is 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.
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.
An app is validly signed with a genuine code signing certificate issued to an unknown company. What does that prove?
RightNot quite The answer is 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.
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.
Which electronic signature has the same legal effect as a handwritten signature throughout the EU?
RightNot quite The answer is 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.
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: the Time-Stamp Protocol, the token format and the TSA certificate rules.
- RFC 5652: Cryptographic Message Syntax, the container behind Authenticode, S/MIME and PDF signatures.
- CA/Browser Forum Code Signing Baseline Requirements: identity checks, key protection, validity limits and timestamp authorities.
- Microsoft SignTool: every sign, timestamp and verify option.
- Apple: Notarizing macOS software before distribution: Developer ID, notarization and stapling.
- Sigstore overview: keyless signing with Fulcio and the Rekor transparency log.
- RFC 8551 and the CA/Browser Forum S/MIME Baseline Requirements: signed and encrypted email.
- eIDAS Regulation (EU) No 910/2014 and Regulation (EU) 2024/1183: 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.
Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.