https://github.com/obfusk/apksigtool
apksigtool - parse/verify/clean/sign android apk (signing block)
https://github.com/obfusk/apksigtool
android apk reproducible-builds signing
Last synced: 6 months ago
JSON representation
apksigtool - parse/verify/clean/sign android apk (signing block)
- Host: GitHub
- URL: https://github.com/obfusk/apksigtool
- Owner: obfusk
- License: agpl-3.0
- Created: 2021-08-25T17:14:18.000Z (about 4 years ago)
- Default Branch: master
- Last Pushed: 2024-10-15T21:04:04.000Z (12 months ago)
- Last Synced: 2025-03-30T21:08:49.088Z (7 months ago)
- Topics: android, apk, reproducible-builds, signing
- Language: Python
- Homepage:
- Size: 2.4 MB
- Stars: 44
- Watchers: 3
- Forks: 3
- Open Issues: 49
-
Metadata Files:
- Readme: README.md
- License: LICENSE.AGPLv3
Awesome Lists containing this project
README
[](https://github.com/obfusk/apksigtool/releases)
[](https://pypi.python.org/pypi/apksigtool)
[](https://pypi.python.org/pypi/apksigtool)
[](https://github.com/obfusk/apksigtool/actions?query=workflow%3ACI)
[](https://www.gnu.org/licenses/agpl-3.0.html)# apksigtool
## parse/verify/clean android apk signing blocks & apks
`apksigtool` is a tool for parsing [android APK Signing
Blocks](https://source.android.com/docs/security/features/apksigning/v2#apk-signing-block)
(either embedded in an APK or extracted as a separate file, e.g. using
[`apksigcopier`](https://github.com/obfusk/apksigcopier)) and verifying [APK
signatures](https://source.android.com/docs/security/features/apksigning). It
can also clean them (i.e. remove everything that's not an APK Signature Scheme
v2/v3 Block or verity padding block), which can be useful for [reproducible
builds](https://reproducible-builds.org).**WARNING: verification is considered EXPERIMENTAL and SHOULD NOT BE RELIED ON, please use
[`apksigner`](https://developer.android.com/studio/command-line/apksigner) instead.**### Parse
Parse tree (some output elided):
```bash
$ apksigtool parse some.apk
PAIR ID: 0x7109871a
APK SIGNATURE SCHEME v2 BLOCK
SIGNER 0
SIGNED DATA
DIGEST 0
SIGNATURE ALGORITHM ID: 0x104 (RSASSA-PKCS1-v1_5 with SHA2-512 digest)
[...]
VERIFIED (1 signer(s))
PAIR ID: 0xf05368c0
APK SIGNATURE SCHEME v3 BLOCK
SIGNER 0
SIGNED DATA
DIGEST 0
SIGNATURE ALGORITHM ID: 0x104 (RSASSA-PKCS1-v1_5 with SHA2-512 digest)
[...]
VERIFIED (1 signer(s))
PAIR ID: 0x42726577
VERITY PADDING BLOCK
```Extracted `APKSigningBlock` instead of APK:
```bash
$ mkdir meta
$ apksigcopier extract some.apk meta
$ apksigtool parse --block meta/APKSigningBlock
[...]
```v1 (JAR) signature (some output elided):
```bash
$ apksigtool parse-v1 some.apk
JAR MANIFEST
VERSION: 1.0
CREATED BY: Android Gradle 7.1.3
BUILT BY: Signflinger
JAR SIGNATURE FILE
FILENAME: META-INF/CERT.SF
VERSION: 1.0
CREATED BY: Android Gradle 7.1.3
SHA256 MANIFEST DIGEST: [...]
ANDROID APK SIGNED: 2
JAR SIGNATURE BLOCK FILE
FILENAME: META-INF/CERT.RSA
CERTIFICATE
[...]
SIGNATURE
VALUE (HEX): [...]
HASH ALGORITHM: SHA256
VERIFIED (1 signature(s))
```#### JSON
NB: elided binary values (`digest`, `fingerprint`, `raw_data`, `signature`) are
represented as hex (e.g. `foo` would be represented as `666f6f`).```bash
$ apksigtool parse --json some.apk
```full JSON output (long, some data elided)
```json
{
"_type": "APKSigningBlock",
"pairs": [
{
"_type": "Pair",
"id": 1896449818,
"length": 1437,
"value": {
"_type": "APKSignatureSchemeBlock",
"signers": [
{
"_type": "V2Signer",
"public_key": {
"_type": "PublicKey",
"public_key_info": {
"_type": "PublicKeyInfo",
"algorithm": "RSA",
"bit_size": 2048,
"fingerprint": "[...]",
"hash_algorithm": null
},
"raw_data": "[...]"
},
"signatures": [
{
"_type": "Signature",
"algoritm_id_info": "RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks",
"signature": "[...]",
"signature_algorithm_id": 259
}
],
"signed_data": {
"_type": "V2SignedData",
"additional_attributes": [
{
"_type": "AdditionalAttribute",
"id": 3203395597,
"is_proof_of_rotation_struct": false,
"is_stripping_protection": true,
"value": "03000000"
}
],
"certificates": [
{
"_type": "Certificate",
"certificate_info": {
"_type": "CertificateInfo",
"fingerprint": "[...]",
"hash_algorithm": "SHA256",
"issuer": "Common Name: [...], Organizational Unit: [...]",
"not_valid_after": "2022-10-27 12:34:56+00:00",
"not_valid_before": "2022-10-26 12:34:56+00:00",
"serial_number": 42,
"signature_algorithm": "RSASSA_PKCS1V15",
"subject": "Common Name: [...], Organizational Unit: [...]"
},
"public_key_info": {
"_type": "PublicKeyInfo",
"algorithm": "RSA",
"bit_size": 2048,
"fingerprint": "[...]",
"hash_algorithm": null
},
"raw_data": "[...]"
}
],
"digests": [
{
"_type": "Digest",
"algoritm_id_info": "RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks",
"digest": "[...]",
"signature_algorithm_id": 259
}
],
"raw_data": "[...]"
}
}
],
"verification_error": null,
"verified": 1,
"version": 2
}
},
{
"_type": "Pair",
"id": 4031998144,
"length": 1437,
"value": {
"_type": "APKSignatureSchemeBlock",
"signers": [
{
"_type": "V3Signer",
"max_sdk": 2147483647,
"min_sdk": 24,
[...]
"signed_data": {
[...]
"max_sdk": 2147483647,
"min_sdk": 24,
[...]
}
}
],
"verification_error": null,
"verified": 1,
"version": 3
}
},
{
"_type": "Pair",
"id": 1114793335,
"length": 1166,
"value": {
"_type": "VerityPaddingBlock"
}
}
]
}
```To extract e.g. pair types or IDs:
```bash
$ apksigtool parse --json some.apk | jq -r '.pairs[].value._type'
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock
$ apksigtool parse --json some.apk | jq -r '.pairs[].id' | awk '{printf "0x%x\n", $1}'
0x7109871a
0xf05368c0
0x42726577
```To extract e.g. public key info:
```bash
$ apksigtool parse --json some.apk | jq '.pairs[].value.signers[]?.public_key.public_key_info'
``````json
{
"_type": "PublicKeyInfo",
"algorithm": "RSA",
"bit_size": 2048,
"fingerprint": "[...]",
"hash_algorithm": null
}
[...]
```To extract e.g. certificate info:
```bash
$ apksigtool parse --json some.apk | jq '.pairs[].value.signers[]?.signed_data.certificates[].certificate_info'
``````json
{
"_type": "CertificateInfo",
"fingerprint": "[...]",
"hash_algorithm": "SHA256",
"issuer": "Common Name: [...], Organizational Unit: [...]",
"not_valid_after": "2022-10-27 12:34:56+00:00",
"not_valid_before": "2022-10-26 12:34:56+00:00",
"serial_number": 42,
"signature_algorithm": "RSASSA_PKCS1V15",
"subject": "Common Name: [...], Organizational Unit: [...]"
}
[...]
```v1 (JAR) signature:
```bash
$ apksigtool parse-v1 --json some.apk | jq -r .manifest.created_by
Android Gradle 7.1.3
```### Verify
**WARNING: verification is considered EXPERIMENTAL and SHOULD NOT BE RELIED ON, please use
[`apksigner`](https://developer.android.com/studio/command-line/apksigner) instead.**```bash
$ apksigtool verify some.apk
WARNING: verification is considered EXPERIMENTAL, please use apksigner instead.
v2 verified (1 signer(s))
v3 verified (1 signer(s))
``````bash
$ apksigtool verify-v1 some.apk
WARNING: verification is considered EXPERIMENTAL, please use apksigner instead.
v1 verified (1 signature(s))
Warning: rollback protections require v2, v3 signature(s) as well.
```### Clean
NB: modifies in place!
```bash
$ cp some.apk cleaned.apk
$ apksigtool clean cleaned.apk
cleaned
$ apksigtool clean cleaned.apk
nothing to clean
```Use `--check` to get errors when parsing or verification (when not
using `--block`) fails:``` bash
$ cp some.apk cleaned.apk
$ apksigtool clean --check cleaned.apk
[...]
```Extracted `APKSigningBlock` instead of APK:
```bash
$ mkdir meta
$ apksigcopier extract some.apk meta
$ apksigtool clean --block meta/APKSigningBlock
cleaned
```### Help
```bash
$ apksigtool --help
$ apksigtool parse --help # verify --help, clean --help, etc.
```## Python API
### APK Signing Block
```python
>>> import apksigtool
>>> _, data = apksigtool.extract_v2_sig(apk)
>>> blk = apksigtool.APKSigningBlock.parse(data) # parse APK Signing Block
>>> blk = apksigtool.parse_apk_signing_block(data) # same as above>>> apksigtool.show_parse_tree(blk) # print parse tree
>>> apksigtool.show_json(blk) # JSON>>> blk.verify(apk) # [EXPERIMENTAL] raises on failure
>>> result = verified, failed = blk.verify_results(apk)
>>> result = apksigtool.verify_apk(apk) # uses .verify_results()
```### Cleaning
```python
>>> import apksigtool
>>> _, data = apksigtool.extract_v2_sig(apk)
>>> data_cleaned = apksigtool.clean_apk_signing_block(data)>>> apksigtool.clean_apk(some_apk) # NB: modifies existing APK!
```### v1 (JAR) signatures
```python
>>> import apksigcopier, apksigtool
>>> meta = tuple(apksigcopier.extract_meta(apk))
>>> sig = apksigtool.JARSignature.parse(meta) # parse v1 signature
>>> sig = apksigtool.parse_apk_v1_signature(meta) # same as above>>> apksigtool.show_v1_signature(sig) # print parse tree
>>> apksigtool.show_json(sig) # JSON>>> result = sig.verify(apk) # [EXPERIMENTAL] raises on failure
```## Tab Completion
NB: the syntax for the environment variable changed in click >= 8.0,
use e.g. `source_bash` instead of `bash_source` for older versions.For Bash, add this to `~/.bashrc`:
```bash
eval "$(_APKSIGTOOL_COMPLETE=bash_source apksigtool)"
```For Zsh, add this to `~/.zshrc`:
```zsh
eval "$(_APKSIGTOOL_COMPLETE=zsh_source apksigtool)"
```For Fish, add this to `~/.config/fish/completions/apksigtool.fish`:
```fish
eval (env _APKSIGTOOL_COMPLETE=fish_source apksigtool)
```## Installing
### From git
NB: this installs the latest development version, not the latest
release.```bash
$ git clone https://github.com/obfusk/apksigtool.git
$ cd apksigtool
$ pip install -e .
```NB: you may need to add e.g. `~/.local/bin` to your `$PATH` in order
to run `apksigtool`.To update to the latest development version:
```bash
$ cd apksigtool
$ git pull --rebase
```## Dependencies
* Python >= 3.8 + [apksigcopier](https://github.com/obfusk/apksigcopier) +
asn1crypto + click + cryptography + pyasn1 + pyasn1-modules + simplejson.### Debian/Ubuntu
```bash
$ apt install apksigcopier python3-{asn1crypto,click,cryptography,pyasn1{,-modules},simplejson}
```## License
[](https://www.gnu.org/licenses/agpl-3.0.html)