Package: apksigcopier
apksigcopiercopy/extract/patch android apk signatures & compare apksapksigcopieris a tool for copyingandroid APK
signaturesfrom a signed APK to an unsigned one (in order to verifyreproducible
builds).  It can also
be used to compare two APKs with different signatures.  Its
command-line tool offers four operations:copy signatures directly from a signed to an unsigned APKextract signatures from a signed APK to a directorypatch previously extracted signatures onto an unsigned APKcompare two APKs with different signaturesExtract$mkdirmeta
$apksigcopierextractsigned.apkmeta
$ls-1meta
8BEA2A77.RSA
8BEA2A77.SF
APKSigningBlock
APKSigningBlockOffset
MANIFEST.MFPatch$apksigcopierpatchmetaunsigned.apkout.apkCopy (Extract & Patch)$apksigcopiercopysigned.apkunsigned.apkout.apkCompare (Copy & Verify)Compare two APKs by copying the signature from the first to a copy of the second
and checking if the resulting APK verifies.This command requiresapksigner.$apksigcopiercomparefoo-from-fdroid.apkfoo-built-locally.apk
$apksigcopiercompare--unsignedfoo.apkfoo-unsigned.apkNB: copying from an APK v1-signed withsignflingerto an APK signed withapksignerworks, whereas the reverse fails; see theFAQ.Help$apksigcopier--help
$apksigcopiercopy--help# extract --help, patch --help, etc.$manapksigcopier# requires the man page to be installedEnvironment VariablesThe following environment variables can be set to1,yes, ortrueto override the default behaviour:setAPKSIGCOPIER_EXCLUDE_ALL_META=1to exclude all metadata filessetAPKSIGCOPIER_COPY_EXTRA_BYTES=1to copy extra bytes after data (e.g. a v2 sig)setAPKSIGCOPIER_SKIP_REALIGNMENT=1to skip realignment of ZIP entriesPython API>>>fromapksigcopierimportdo_extract,do_patch,do_copy,do_compare>>>do_extract(signed_apk,output_dir,v1_only=NO)>>>do_patch(metadata_dir,unsigned_apk,output_apk,v1_only=NO)>>>do_copy(signed_apk,unsigned_apk,output_apk,v1_only=NO)>>>do_compare(first_apk,second_apk,unsigned=False)You can useFalse,None, andTrueinstead ofNO,AUTO, andYESrespectively.The following global variables (which default toFalse), can be set
to override the default behaviour:setexclude_all_meta=Trueto exclude all metadata filessetcopy_extra_bytes=Trueto copy extra bytes after data (e.g. a v2 sig)setskip_realignment=Trueto skip realignment of ZIP entriesFAQWhat kind of signatures does apksigcopier support?It currently supports v1 + v2 + v3 (which is a variant of v2).It should also support v4, since these are stored in a separate file
(and require a complementary v2/v3 signature).When using theextractcommand, the v2/v3 signature is saved asAPKSigningBlock+APKSigningBlockOffset.How does patching work?First it copies the APK exactly likeapksignerwould when signing it,
including re-aligning ZIP entries and skipping existing v1 signature files.Then it adds the extracted v1 signature files (.SF,.RSA/.DSA/.EC,MANIFEST.MF) to the APK, using the correct ZIP metadata (either the same
metadata asapksignerwould, or fromdifferences.json).And lastly it inserts the extracted APK Signing Block at the correct offset
(adding zero padding if needed) and updates the central directory (CD) offset in
the end of central directory (EOCD) record.For more information about the ZIP file format, see e.g.the Wikipedia
article.What does the "APK Signing Block offset < central directory offset" error mean?It means thatapksigcopiercan't insert the APK Signing Block at the required
location, since that offset is in the middle of the ZIP data (instead of right
after the data, before the central directory).In other words: the APK you are trying to copy the signature to is larger than
the one the signature was copied from.  Thus the signature cannot be copied (and
could never have been valid for the APK you are trying to copy it to).In the context of verifyingreproducible builds,
getting this error almost certainly means the build was not reproducible.What about APKs signed by gradle/zipflinger/signflinger instead of apksigner?Compared to APKs signed byapksigner, APKs signed with a v1 signature byzipflinger/signflinger(e.g. usinggradle) have different ZIP metadata --create_system,create_version,external_attr,extract_version,flag_bits-- andcompresslevelfor the v1 signature files (.SF,.RSA/.DSA/.EC,MANIFEST.MF); they also usually have a 132-byte virtual
entry at the start as well.Recent versions ofapksigcopierwill detect these ZIP metadata differences and
the virtual entry (if any);extractwill save them in adifferences.jsonfile (if they exist), whichpatchwill read (if it exists);copyandcomparesimply pass the same information along internally.CAVEAT for compareNB: becausecomparecopies from the first APK to the second, it will fail when
only the second APK is v1-signed withzipflinger/signflinger; e.g.$comparefoo-signflinger.apkfoo-apksigner.apk# copies virtual entry; works$comparefoo-apksigner.apkfoo-signflinger.apk# only 2nd APK has virtual entryDOESNOTVERIFY[...]Error:failedtoverify/tmp/.../output.apk.What are these virtual entries?A virtual entry is a ZIP entry with an empty filename, an extra field filled
with zero bytes, and no corresponding central directory entry (so it should be
effectively invisible to most ZIP tools).Whenzipflingerdeletes an entry it leaves a "hole" in the archive when there
remain non-deleted entries after it.  It later fills these "holes" with virtual
entries.There is usually a 132-byte virtual entry at the start of an APK signed with a
v1 signature bysignflinger/zipflinger; almost certainly this is a default
manifest ZIP entry created at initialisation, deleted (from the central
directory but not from the file) during v1 signing, and eventually replaced by a
virtual entry.Depending on what value ofCreated-ByandBuilt-Bywere used for the default
manifest, this virtual entry may be a different size;apksigcopiersupports
any size between 30 and 4096 bytes.InstallingDebianOfficial packages are available inDebianandUbuntu.$aptinstallapksigcopierYou can also manually build a Debian package using thedebian/sidbranch, or download a pre-built.debvia GitHub releases.NixOS & Arch LinuxOfficial packages are also available innixpkgsandArch Linux(and derivatives).Using pip$pipinstallapksigcopierNB: depending on your system you may need to use e.g.pip3 --userinstead of justpip.From gitNB: this installs the latest development version, not the latest
release.$gitclonehttps://github.com/obfusk/apksigcopier.git
$cdapksigcopier
$pipinstall-e.NB: you may need to add e.g.~/.local/binto your$PATHin order
to runapksigcopier.To update to the latest development version:$cdapksigcopier
$gitpull--rebaseDependenciesPython >= 3.7 + click.Thecomparecommand also requiresapksigner.Debian/Ubuntu$aptinstallpython3-click
$aptinstallapksigner# only needed for the compare commandLicense