skip to content

Explain the files signing adds under META-INF — the manifest digests, the .SF file, and the .RSA/.DSA/.EC block — and how the runtime uses them to verify.

level: seniorimportance: should knowfreq 24%

answer

  1. MANIFEST.MF = per-file digests (SHA-256-Digest)
  2. .SF = digest of the manifest (and its sections)
  3. .RSA/.DSA/.EC = actual signature + cert chain (PKCS#7)
  4. verify outward: file → manifest → signature → cert
  5. .SF indirection lets unsigned entries be added later

basics

~20 s

Signing adds per-file hashes to MANIFEST.MF, a NAME.SF file holding a hash of the manifest, and a NAME.RSA/DSA/EC block containing the actual digital signature plus the certificate. Verification re-hashes entries and checks the signature against the certificate.

solid answer

~50 s

Signing layers three things in META-INF. First, MANIFEST.MF gets a per-entry section for each file with a digest header like 'SHA-256-Digest: <hash>'. Second, a signature file NAME.SF mirrors those entries but stores a digest of each manifest entry plus a digest of the whole manifest header — it signs the manifest indirectly, which lets unrelated entries be added without re-signing everything. Third, NAME.RSA (or .DSA/.EC, named after the key algorithm) is the signature block: the actual digital signature computed over the .SF file, bundled with the signer's certificate chain. Verification works outward: recompute each entry's digest and compare to MANIFEST.MF (file integrity); recompute the manifest's digest and compare to the .SF (manifest integrity); then verify the .SF's signature against the certificate's public key in the block (authenticity). All three layers must check out for 'jar verified.'.

code

java · 16 lines
java
// META-INF/MANIFEST.MF (per-file digest)
// Name: com/example/App.class
// SHA-256-Digest: K7s9...=
//
// META-INF/MYALIAS.SF (digest OF the manifest, not the file)
// Signature-Version: 1.0
// SHA-256-Digest-Manifest: 9af2...=
// Name: com/example/App.class
// SHA-256-Digest: q1Zb...=   <-- hash of App.class's manifest section
//
// META-INF/MYALIAS.RSA  (binary PKCS#7 signature block)
//   = signature over MYALIAS.SF  +  signer certificate chain
//
// Verify order: recompute file hash -> match MANIFEST.MF
//               recompute manifest hash -> match .SF
//               check .SF signature -> against cert in .RSA

go deeper

for a junior

Knows signing adds files under META-INF and that they let the JVM check the JAR wasn't changed.

for a middle

Names the three layers — manifest digests, .SF, and .RSA block — and roughly what each holds.

for a senior

Explains the digest chain, the .SF-indirection rationale (and the unsigned-entry subtlety), the PKCS#7 block, and the outward verification order.

for a principal

Reasons about multi-signer scenarios, the security implications of partial signing in dependency supply chains, and how to enforce full-signature policies.

## Where the artifacts live Everything signing-related sits in the **`META-INF/`** directory of the JAR. Three kinds of files participate, forming a chain of digests so a single asymmetric signature can cover the whole archive efficiently. ## Layer 1 — `MANIFEST.MF` per-entry digests The manifest already lists the archive's entries. Signing adds, for **each** signed file, a per-entry section like: ``` Name: com/example/App.class SHA-256-Digest: K7s9...base64...= ``` The **digest** is the base64 of the file's cryptographic hash. This is the ground truth for *file* integrity: change `App.class` and its recomputed hash no longer matches. ## Layer 2 — `NAME.SF` (signature file) `NAME` is the signer's alias (e.g. `MYALIAS.SF`). It looks like a manifest but instead stores, per entry, a digest of **the corresponding manifest section** (not of the file itself): ``` Signature-Version: 1.0 SHA-256-Digest-Manifest: <hash of the whole MANIFEST.MF> ... Name: com/example/App.class SHA-256-Digest: <hash of App.class's manifest SECTION> ``` Why this indirection? It means the signature ultimately covers the **manifest**, not each file directly. A key consequence: you can **add new (unsigned) entries** to the JAR later without invalidating the existing signature — the verifier only insists that *signed* entries match. (This is also a subtlety: tools must treat unsigned added entries as untrusted.) The `SHA-256-Digest-Manifest` header lets a verifier validate the whole manifest in one shot. ## Layer 3 — `NAME.RSA` / `NAME.DSA` / `NAME.EC` (signature block) This binary file is the **signature block**. Its extension names the signing key's algorithm (RSA, DSA, or EC/ECDSA). It contains: - the **digital signature** computed by the private key over the bytes of the `.SF` file, and - the signer's **certificate chain** (the public certificate, plus any intermediates up toward a CA root). It is encoded in **PKCS#7 / CMS** (a standard signed-data container). Because it carries the certificate, the verifier needs nothing external to check authenticity — only to *decide whether to trust* that certificate. ## How verification flows (outermost → innermost) 1. **File integrity:** recompute each signed file's hash; compare to `MANIFEST.MF`. Mismatch → file tampered. 2. **Manifest integrity:** recompute the manifest section/whole-manifest digests; compare to `NAME.SF`. Mismatch → manifest tampered. 3. **Authenticity:** verify the digital signature in `NAME.RSA` over `NAME.SF` using the certificate's public key. Pass → signed by that key's holder. 4. **Certificate checks:** validity period (helped by a timestamp), and chain to a trusted root if CA-backed. Only if 1–3 succeed does `jarsigner -verify` print `jar verified.`. Step 4 governs *trust*, which is policy. ## Multiple signers A JAR can be signed by several parties: each adds its own `NAME.SF` + block (different aliases). Each contributes per-entry digests, so a verifier can check any/all signers. ## Term glossary - **Digest / hash:** fixed-length fingerprint of bytes; any change alters it. - **PKCS#7 / CMS:** standard format for a signature-plus-certificates blob. - **Certificate chain:** the signer's cert plus intermediates linking to a trusted root. ## Contrast with sealing These files are entirely about **signing**. Sealing, by contrast, adds only a plain `Sealed: true` header to the manifest — no `.SF`, no `.RSA`, no cryptography.

  • Why does the .SF file store a digest of the manifest rather than signing each class file directly?
    Indirection through the manifest lets one signature cover the whole archive and lets unsigned entries be added later without re-signing. The signature binds the .SF, the .SF binds the manifest, and the manifest binds each file's hash — a digest chain. A side effect is that newly added unsigned entries are simply unverified, so tooling must treat them as untrusted.
  • Why might the signature block be named .EC instead of .RSA?
    The extension reflects the signing key's algorithm: .RSA for RSA keys, .DSA for DSA, and .EC (ECDSA) for elliptic-curve keys. The content is otherwise the same kind of PKCS#7/CMS signed-data block carrying the signature and certificate chain.

saying these in an interview costs you the question

  • Thinking the .SF file contains the digital signature — the signature lives in the .RSA/.DSA/.EC block; the .SF holds manifest digests.
  • Saying the signature directly signs every class — it signs the .SF, which chains through the manifest to the files.
  • Assuming a verified signature also covers any unsigned entries added afterward — it does not.
  • Confusing these signing files with sealing, which adds only a plain manifest header.

context