Android has four ways to sign an APK. Scheme v1 is ordinary JAR signing, in files under
META-INF/ (see the JAR page). Schemes v2
(Android 7.0), v3 (Android 9) and v3.1 (Android 13) live in the APK Signing
Block, a binary block inserted into the ZIP
between the last entry’s data and the central directory. Scheme v4 is a separate
.idsig file. This page follows the Android Open Source Project’s
documentation of the schemes and the constants in apksig, the library
behind apksigner. All numbers are little-endian.
APK Signing Block
To find the block, read the ZIP end of central directory record, take the central directory’s offset from it, and read the 24 bytes just before that offset: a 64-bit size and the 16-byte magic. The size gives the block’s start.
| Offset | Size | Field | Meaning |
|---|---|---|---|
0 | 8 | size | Size of the block in bytes, not counting this field. |
8 | varies | pairs | A sequence of ID–value pairs (below). |
| end − 24 | 8 | size | The same size again; the two must match. |
| end − 16 | 16 | magic | ASCII APK Sig Block 42 (41 50 4B 20 53 69 67 20 42 6C 6F 63 6B 20 34 32). |
Each pair:
| Offset | Size | Field | Meaning |
|---|---|---|---|
0 | 8 | length | Length of the rest of the pair (ID and value). |
8 | 4 | ID | What the value is; see IDs. |
12 | length − 4 | value | The block for that ID. |
Verifiers ignore IDs they do not know. A verifier also checks that the central directory is followed directly by the end of central directory record and that nothing follows it.
Block IDs
| ID | Block |
|---|---|
0x7109871A | APK Signature Scheme v2 |
0xF05368C0 | APK Signature Scheme v3 |
0x1B93AD61 | APK Signature Scheme v3.1 |
0x42726577 | Verity padding (VERITY_PADDING_BLOCK_ID in apksig, which also defines a 4096-byte page alignment); its exact use is not documented |
0x6DFF800D | Source stamp, version 2 |
0x2B09189E | Source stamp, version 1 |
Other IDs may appear; verifiers skip any they do not know.
Scheme numbers
apksig numbers the schemes 0 (source stamp), 1 (JAR signing), 2, 3, 31
(v3.1) and 4. The X-Android-APK-Signed attribute in a v1
.SF file lists the newer schemes also present (for example
2, 3); a verifier that supports one of them must reject the APK if that
scheme’s block is missing, which stops an attacker stripping the stronger signature.
APK Signature Scheme v2 block
Every “length-prefixed” item below has a 32-bit length in front of it, and a “sequence” is a length-prefixed run of length-prefixed items.
| Item | Type | Meaning |
|---|---|---|
| signers | sequence | One or more signers. |
| signed data | length-prefixed | The bytes that the signatures sign: |
| digests | sequence | Each: signature algorithm ID (uint32) and a length-prefixed digest of the APK’s contents. |
| certificates | sequence | X.509 certificates, DER; the first is the signer’s. |
| additional attributes | sequence | Each: ID (uint32) and value. See attributes. |
| signatures | sequence | Each: signature algorithm ID (uint32) and a length-prefixed signature over signed data. |
| public key | length-prefixed | SubjectPublicKeyInfo, DER. Must match the first certificate. |
Verification, for each signer: choose the strongest supported algorithm in signatures, verify that signature over signed data with the public key, check that digests and signatures list exactly the same algorithm IDs, compute the APK’s digest and compare it, and check the first certificate’s key matches the public key. There must be at least one signer. If a v2 block is present but fails, the APK is rejected; Android does not fall back to v1.
Signature algorithm IDs
| ID | Algorithm | Content digest | From |
|---|---|---|---|
0x0101 | RSASSA-PSS with SHA-256, MGF1 SHA-256, 32-byte salt, trailer 0xBC | Chunked SHA-256 | Android 7.0 |
0x0102 | RSASSA-PSS with SHA-512, MGF1 SHA-512, 64-byte salt, trailer 0xBC | Chunked SHA-512 | Android 7.0 |
0x0103 | RSASSA-PKCS1-v1_5 with SHA-256 | Chunked SHA-256 | Android 7.0 |
0x0104 | RSASSA-PKCS1-v1_5 with SHA-512 | Chunked SHA-512 | Android 7.0 |
0x0201 | ECDSA with SHA-256 | Chunked SHA-256 | Android 7.0 |
0x0202 | ECDSA with SHA-512 | Chunked SHA-512 | Android 7.0 |
0x0301 | DSA with SHA-256 (also used for deterministic DSA) | Chunked SHA-256 | Android 7.0 |
0x0421 | RSASSA-PKCS1-v1_5 with SHA-256 | Verity SHA-256 (4 KB Merkle tree) | Android 9 |
0x0423 | ECDSA with SHA-256 | Verity SHA-256 | Android 9 |
0x0425 | DSA with SHA-256 | Verity SHA-256 | Android 9 |
Supported keys: RSA of 1024, 2048, 4096, 8192 or 16384 bits; EC on NIST P-256, P-384 or P-521; DSA of 1024, 2048 or 3072 bits.
The chunked content digest
The file is seen as four parts: (1) the ZIP entries, from offset 0 to the signing block; (2) the signing block; (3) the central directory; (4) the end of central directory record. Parts 1, 3 and 4 are digested; part 2 is protected by the signatures inside it.
- Split each of parts 1, 3 and 4 into 1 MiB (220-byte) chunks; the last chunk of each part may be shorter.
- Digest each chunk as
0xA5, the chunk length (uint32), then the chunk’s bytes. - The top-level digest is over
0x5A, the number of chunks (uint32), then all chunk digests in file order.
While digesting part 4, the central directory offset in the end record is replaced by
the offset of the signing block, so the value is the same as before the block was
inserted. The verity algorithms (0x0421, 0x0423,
0x0425) use a different content digest, “verity chunked SHA-256”, over
4 KB blocks (as the v4 page describes it); its exact byte layout is not documented on
the Android pages and is not given here.
Additional attributes
| ID | Used in | Value |
|---|---|---|
0xBEEFF00D | v2 | Stripping protection (STRIPPING_PROTECTION_ATTR_ID); its value is not documented on the Android pages. |
0x3BA06F8C | v3, v3.1 | Proof of rotation. |
0x559F8B02 | v3, v3.1 | Rotation minimum SDK version (ROTATION_MIN_SDK_VERSION_ATTR_ID); which signer carries it is not documented. |
0xC2A6B3BA | v3, v3.1 | Rotation on development release (ROTATION_ON_DEV_RELEASE_ATTR_ID); not documented further. |
APK Signature Scheme v3 and v3.1 blocks
A v3 block has the same outer shape as v2 (a sequence of signers), with SDK ranges added so that different signers can apply to different platform versions, and the proof-of-rotation attribute so that a new key can replace an old one.
| Item | Type | Meaning |
|---|---|---|
| signers | sequence | One or more signers. |
| signed data | length-prefixed | The signed bytes: |
| digests | sequence | Algorithm ID and digest, as in v2. |
| certificates | sequence | X.509 certificates, DER. |
| minSDK | uint32 | Ignore this signer on platform versions below this API level. |
| maxSDK | uint32 | Ignore it above this API level. |
| additional attributes | sequence | ID and value pairs. |
| minSDK | uint32 | Copy of the signed value; must match. |
| maxSDK | uint32 | Copy of the signed value; must match. |
| signatures | sequence | Algorithm ID and signature over signed data. |
| public key | length-prefixed | SubjectPublicKeyInfo, DER. |
v3.1 (block 0x1B93AD61) is supported from Android 13
(apksig’s MIN_SDK_WITH_V31_SUPPORT), and apksig’s default rotation
minimum SDK is also Android 13; v3 itself needs Android 9. The Android pages do not
describe v3.1, so its layout (apksig writes it with the v3 signer code) is not
confirmed here.
Proof of rotation
The proof of rotation (signing certificate lineage) lists every key the app has been signed with, oldest first, each signed by the one before:
| Item | Type | Meaning |
|---|---|---|
| version | uint32 | 1. |
| nodes | length-prefixed, repeated to the end | One per certificate: |
| signed data | length-prefixed | A length-prefixed DER certificate, then the signature algorithm ID (uint32) the previous node used to sign this one (0 in the first node). |
| flags | uint32 | What the app may still do with this older certificate (below). |
| signature algorithm | uint32 | Algorithm this node’s key uses to sign the next node. |
| signature | length-prefixed | The previous node’s key’s signature over this node’s signed data (empty in the first node). |
| Flag | Name | Meaning |
|---|---|---|
1 | INSTALLED_DATA | Accept an update of an installed app signed with this certificate and keep its data. |
2 | SHARED_USER_ID | Allow a shared user ID with apps signed with this certificate. |
4 | PERMISSION | Grant signature permissions to apps signed with this certificate. |
8 | ROLLBACK | Allow updates back to this certificate (which undoes the benefit of rotating). |
16 | AUTH | Keep account-authenticator access tied to this certificate. |
apksigner rotate keeps the lineage in a file of its own: a magic
0x3EFF39D1, a version (1), a length, then the encoding above.
Source stamp
A source stamp records which store or build system produced the APK. It is a block in
the signing block (0x6DFF800D for version 2, 0x2B09189E for
version 1) and a ZIP entry stamp-cert-sha256 holding the SHA-256 of the stamp certificate.
Its signed data may hold a stamp time attribute (0xE43C5946) and a proof of
rotation for the stamp key (0x9D6303F7). The Android Open Source Project
has no separate description of the stamp’s inner layout; these values are from
apksig.
APK Signature Scheme v4 (.idsig)
v4 supports incremental installation (streaming an app while it installs). The
signature is a separate file, <name>.apk.idsig, and needs a v2 or v3
signature in the APK as well. Every “bytes” item is an int32 length and the bytes.
| Item | Type | Meaning |
|---|---|---|
| version | int32 | 2, the only version in use. |
| hashing_info | bytes | Contains: |
| hash_algorithm | int32 | 1 = SHA-256 (the only value). |
| log2_blocksize | int8 | 12 = 4096-byte blocks (the only value). |
| salt | bytes | fs-verity salt, up to 32 bytes. |
| raw_root_hash | bytes | Salted digest of the first Merkle tree page. |
| signing_info | bytes | Contains: |
| apk_digest | bytes | A content digest taken from the APK’s v3 or v2 block, the first available of: v3 chunked SHA-512, v3 verity SHA-256, v3 chunked SHA-256, v2 SHA-512, v2 SHA-256. |
| x509_certificate | bytes | DER certificate. |
| additional_data | bytes | Free-form data. |
| public_key | bytes | DER public key; must match the certificate. |
| signature_algorithm_id | int32 | One of the algorithm IDs. |
| signature | bytes | Signature over the v4 signed data. |
| merkle_tree | bytes, optional | The whole fs-verity Merkle tree of the APK. With it the signature is “complete”; without it, “stripped”. |
The v4 signed data, which is what signature covers, is: a size (int32), the APK’s file size (int64), then hash_algorithm, log2_blocksize, salt, raw_root_hash, apk_digest, x509_certificate and additional_data, encoded as above.
Sources
Android Open Source Project: “APK Signature Scheme v2”, “APK Signature Scheme v3” and
“APK Signature Scheme v4” (source.android.com). AOSP tools/apksig:
ApkSigningBlockUtils (magic, padding ID, scheme numbers, chunk size),
V2SchemeConstants, V3SchemeConstants,
SignatureAlgorithm (all algorithm IDs and minimum platform versions),
SourceStampConstants, SigningCertificateLineage (flags,
lineage file) and V3SigningCertificateLineage (node encoding). Values
that come only from apksig constants, without a documented meaning, are marked as
such.