A JAR is signed by jarsigner or the java.security API. The
signed JAR is the original with three changes: a digest of each entry added to the
manifest, and two new files in
META-INF/ per signer, a signature file and a
signature block file. Android’s APK signature scheme v1 is exactly
this method (see APK signing). This page
follows the JAR File Specification.
Signature-related files
| File | Contents |
|---|---|
META-INF/MANIFEST.MF | The manifest, with an individual section and x-Digest attribute for every signed entry. |
META-INF/NAME.SF | The signature file: digests of the manifest and of its sections. |
META-INF/NAME.RSA | Signature block for an RSA or RSASSA-PSS key. |
META-INF/NAME.DSA | Signature block for a DSA key. |
META-INF/NAME.EC | Signature block for an EC or EdDSA key. |
META-INF/SIG-NAME.ext | Signature block for any other algorithm; the matching signature file is then SIG-NAME.SF. The extension is 1 to 3 letters or digits. |
These names count only directly in META-INF/ (not in its sub-folders),
and are matched without regard to case. They are not themselves signed; every other
entry present when the JAR was signed is.
The block file has the same base name NAME as its .SF. How
jarsigner chooses the name is described in its own manual, not in the
specification, and is not covered here.
The signature file (.SF)
Same syntax as the manifest. Its main section holds:
| Attribute | Meaning |
|---|---|
Signature-Version | First; 1.0. |
Created-By | The tool that signed it. |
x-Digest-Manifest | Base64 digest of the whole manifest file with algorithm x. |
x-Digest-Manifest-Main-Attributes | Digest of the manifest’s main section only. |
Each individual section names an entry that is also in the manifest and gives
x-Digest: the digest of that entry’s section of the
manifest, not of the entry’s data. Manifest entries not listed in the
.SF are not covered by that signer.
x is a standard MessageDigest name; no algorithm is required,
but verifiers must support at least one of SHA-256 and
SHA1.
Signature-Version: 1.0
Created-By: 21.0.2 (Eclipse Adoptium)
SHA-256-Digest-Manifest: (base64 SHA-256 of MANIFEST.MF)
SHA-256-Digest-Manifest-Main-Attributes: (base64 SHA-256 of its main section)
Name: common/class1.class
SHA-256-Digest: (base64 SHA-256 of that manifest section)
The signature block file
A PKCS #7 structure holding the digital signature of the .SF file. Where
the format can refer to external signed data, it refers to the .SF;
where it cannot, it carries a copy,
which a verifier should compare with the .SF. Each .SF may
have several signatures, but they should all come from the same entity.
Verification
- Check the signature in the block file over the
.SFfile. This proves only that the.SFis genuine. - If the
.SFhasx-Digest-Manifest, compare it with a digest of the whole manifest; with several, at least one must match. - If that is missing or does not match (for example because entries were added to
the JAR after signing): check
x-Digest-Manifest-Main-Attributesagainst the main section if present, then check every section digest in the.SFagainst the matching manifest section. Any mismatch fails. - For each entry, compare its digest in the manifest with a digest of the entry’s data. Any mismatch fails.
Entries added after signing change the manifest digest, but verification still succeeds through step 3; an entry changed after signing fails verification.
Sources
JAR File Specification, Java SE 21: “The META-INF directory”, “Signed JAR File”, “Signature File”, “Digital Signatures” and “Signature Validation”.