Skip to content

apksigtool - parse/verify/clean/sign android apk (signing block)

License

Notifications You must be signed in to change notification settings

obfusk/apksigtool

Folders and files

NameName
Last commit message
Last commit date

Latest commit

82b7be0 · Nov 18, 2022
Nov 9, 2022
Nov 18, 2022
Oct 29, 2022
Nov 18, 2022
Aug 25, 2021
Nov 9, 2022
Oct 21, 2022
Aug 25, 2021
Aug 25, 2021
Oct 29, 2022
Nov 9, 2022
Nov 8, 2022
Nov 6, 2022

Repository files navigation

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+