Archived
1
1
Fork
You've already forked apksigtool
0
[github mirror] apksigtool - parse/verify/clean/sign android apk (signing block) https://github.com/obfusk/apksigtool
This repository has been archived on 2025年05月12日. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
  • Python 91.9%
  • Shell 5%
  • Makefile 3.1%
Find a file
2022年11月18日 23:58:14 +01:00
.github/workflows CI: install coverage 2022年11月09日 01:51:30 +01:00
apksigtool properly parse ASN.1 in .SF [ closes #47 ] 2022年11月18日 23:58:14 +01:00
bin add py.typed (required re-ordering files) 2022年10月29日 21:51:42 +02:00
test properly parse ASN.1 in .SF [ closes #47 ] 2022年11月18日 23:58:14 +01:00
.flake8 not a bad start 2021年08月25日 19:18:19 +02:00
.gitignore make: coverage++ [skip ci] 2022年11月09日 01:24:53 +01:00
.gitmodules move tests APKs to submodule 2022年10月21日 17:52:22 +02:00
.pylintrc not a bad start 2021年08月25日 19:18:19 +02:00
LICENSE.AGPLv3 rename LICENSE 2021年08月25日 19:15:01 +02:00
Makefile make: coverage++ [skip ci] 2022年11月09日 01:24:53 +01:00
MANIFEST.in add py.typed (required re-ordering files) 2022年10月29日 21:51:42 +02:00
README.md README: update dependencies [skip ci] 2022年11月08日 23:56:45 +01:00
setup.py v1 verification & more 2022年11月06日 22:48:59 +01:00

GitHub Release PyPI Version Python Versions CI AGPLv3+

apksigtool

parse/verify/clean android apk signing blocks & apks

apksigtool is a tool for parsing android APK Signing Blocks (either embedded in an APK or extracted as a separate file, e.g. using apksigcopier) and verifying APK signatures. 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.

WARNING: verification is considered EXPERIMENTAL and SHOULD NOT BE RELIED ON, please use apksigner instead.

Parse

Parse tree (some output elided):

$ 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:

$ mkdir meta
$ apksigcopier extract some.apk meta
$ apksigtool parse --block meta/APKSigningBlock
[...]

v1 (JAR) signature (some output elided):

$ 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).

$ apksigtool parse --json some.apk
full JSON output (long, some data elided)
{
 "_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:

$ 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:

$ apksigtool parse --json some.apk | jq '.pairs[].value.signers[]?.public_key.public_key_info'
{
 "_type": "PublicKeyInfo",
 "algorithm": "RSA",
 "bit_size": 2048,
 "fingerprint": "[...]",
 "hash_algorithm": null
}
[...]

To extract e.g. certificate info:

$ apksigtool parse --json some.apk | jq '.pairs[].value.signers[]?.signed_data.certificates[].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: [...]"
}
[...]

v1 (JAR) signature:

$ 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 instead.

$ apksigtool verify some.apk
WARNING: verification is considered EXPERIMENTAL, please use apksigner instead.
v2 verified (1 signer(s))
v3 verified (1 signer(s))
$ 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!

$ 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:

$ cp some.apk cleaned.apk
$ apksigtool clean --check cleaned.apk
[...]

Extracted APKSigningBlock instead of APK:

$ mkdir meta
$ apksigcopier extract some.apk meta
$ apksigtool clean --block meta/APKSigningBlock
cleaned

Help

$ apksigtool --help
$ apksigtool parse --help # verify --help, clean --help, etc.

Python API

APK Signing Block

>>> 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

>>> 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

>>> 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:

eval "$(_APKSIGTOOL_COMPLETE=bash_source apksigtool)"

For Zsh, add this to ~/.zshrc:

eval "$(_APKSIGTOOL_COMPLETE=zsh_source apksigtool)"

For Fish, add this to ~/.config/fish/completions/apksigtool.fish:

eval (env _APKSIGTOOL_COMPLETE=fish_source apksigtool)

Installing

From git

NB: this installs the latest development version, not the latest release.

$ 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:

$ cd apksigtool
$ git pull --rebase

Dependencies

  • Python >= 3.8 + apksigcopier + asn1crypto + click + cryptography + pyasn1 + pyasn1-modules + simplejson.

Debian/Ubuntu

$ apt install apksigcopier python3-{asn1crypto,click,cryptography,pyasn1{,-modules},simplejson}

License

AGPLv3+