mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-11-20 13:50:12 +01:00
add common functions for dealing with apk signatures
This commit is contained in:
parent
45688bfe42
commit
efb0bf6ee5
@ -35,6 +35,7 @@ import hashlib
|
||||
import socket
|
||||
import base64
|
||||
import zipfile
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as XMLElementTree
|
||||
|
||||
from binascii import hexlify
|
||||
@ -2015,6 +2016,18 @@ def place_srclib(root_dir, number, libpath):
|
||||
apk_sigfile = re.compile(r'META-INF/[0-9A-Za-z]+\.(SF|RSA|DSA|EC)')
|
||||
|
||||
|
||||
def signer_fingerprint_short(sig):
|
||||
"""Obtain shortened sha256 signing-key fingerprint for pkcs7 signature.
|
||||
|
||||
Extracts the first 7 hexadecimal digits of sha256 signing-key fingerprint
|
||||
for a given pkcs7 signature.
|
||||
|
||||
:param sig: Contents of an APK signature.
|
||||
:returns: shortened signing-key fingerprint.
|
||||
"""
|
||||
return signer_fingerprint(sig)[:7]
|
||||
|
||||
|
||||
def signer_fingerprint(sig):
|
||||
"""Obtain sha256 signing-key fingerprint for pkcs7 signature.
|
||||
|
||||
@ -2052,6 +2065,18 @@ def apk_signer_fingerprint(apk_path):
|
||||
return signer_fingerprint(cert)
|
||||
|
||||
|
||||
def apk_signer_fingerprint_short(apk_path):
|
||||
"""Obtain shortened sha256 signing-key fingerprint for APK.
|
||||
|
||||
Extracts the first 7 hexadecimal digits of sha256 signing-key fingerprint
|
||||
for a given pkcs7 APK.
|
||||
|
||||
:param apk_path: path to APK
|
||||
:returns: shortened signing-key fingerprint
|
||||
"""
|
||||
return apk_signer_fingerprint(apk_path)[:7]
|
||||
|
||||
|
||||
def metadata_get_sigdir(appid, vercode=None):
|
||||
"""Get signature directory for app"""
|
||||
if vercode:
|
||||
@ -2060,6 +2085,94 @@ def metadata_get_sigdir(appid, vercode=None):
|
||||
return os.path.join('metadata', appid, 'signatures')
|
||||
|
||||
|
||||
def metadata_find_signing_files(appid, vercode):
|
||||
"""Gets a list of singed manifests and signatures.
|
||||
|
||||
:param appid: id string of that app
|
||||
:param vercode: version code of that app
|
||||
:returns: a list of triplets for each signing key with following paths:
|
||||
(signature_file, singed_file, manifest_file)
|
||||
"""
|
||||
ret = []
|
||||
sigdir = metadata_get_sigdir(appid, vercode)
|
||||
sigs = glob.glob(os.path.join(sigdir, '*.DSA')) + \
|
||||
glob.glob(os.path.join(sigdir, '*.EC')) + \
|
||||
glob.glob(os.path.join(sigdir, '*.RSA'))
|
||||
extre = re.compile('(\.DSA|\.EC|\.RSA)$')
|
||||
for sig in sigs:
|
||||
sf = extre.sub('.SF', sig)
|
||||
if os.path.isfile(sf):
|
||||
mf = os.path.join(sigdir, 'MANIFEST.MF')
|
||||
if os.path.isfile(mf):
|
||||
ret.append((sig, sf, mf))
|
||||
return ret
|
||||
|
||||
|
||||
def metadata_find_developer_signing_files(appid, vercode):
|
||||
"""Get developer signature files for specified app from metadata.
|
||||
|
||||
:returns: A triplet of paths for signing files from metadata:
|
||||
(signature_file, singed_file, manifest_file)
|
||||
"""
|
||||
allsigningfiles = metadata_find_signing_files(appid, vercode)
|
||||
if allsigningfiles and len(allsigningfiles) == 1:
|
||||
return allsigningfiles[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def apk_strip_signatures(signed_apk, strip_manifest=False):
|
||||
"""Removes signatures from APK.
|
||||
|
||||
:param signed_apk: path to apk file.
|
||||
:param strip_manifest: when set to True also the manifest file will
|
||||
be removed from the APK.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
tmp_apk = os.path.join(tmpdir, 'tmp.apk')
|
||||
os.rename(signed_apk, tmp_apk)
|
||||
with ZipFile(tmp_apk, 'r') as in_apk:
|
||||
with ZipFile(signed_apk, 'w') as out_apk:
|
||||
for f in in_apk.infolist():
|
||||
if not apk_sigfile.match(f.filename):
|
||||
if strip_manifest:
|
||||
if f.filename != 'META-INF/MANIFEST.MF':
|
||||
buf = in_apk.read(f.filename)
|
||||
out_apk.writestr(f.filename, buf)
|
||||
else:
|
||||
buf = in_apk.read(f.filename)
|
||||
out_apk.writestr(f.filename, buf)
|
||||
|
||||
|
||||
def apk_implant_signatures(apkpath, signaturefile, signedfile, manifest):
|
||||
"""Implats a signature from out metadata into an APK.
|
||||
|
||||
Note: this changes there supplied APK in place. So copy it if you
|
||||
need the original to be preserved.
|
||||
|
||||
:param apkpath: location of the apk
|
||||
"""
|
||||
# get list of available signature files in metadata
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# orig_apk = os.path.join(tmpdir, 'orig.apk')
|
||||
# os.rename(apkpath, orig_apk)
|
||||
apkwithnewsig = os.path.join(tmpdir, 'newsig.apk')
|
||||
with ZipFile(apkpath, 'r') as in_apk:
|
||||
with ZipFile(apkwithnewsig, 'w') as out_apk:
|
||||
for sig_file in [signaturefile, signedfile, manifest]:
|
||||
out_apk.write(sig_file, arcname='META-INF/' +
|
||||
os.path.basename(sig_file))
|
||||
for f in in_apk.infolist():
|
||||
if not apk_sigfile.match(f.filename):
|
||||
if f.filename != 'META-INF/MANIFEST.MF':
|
||||
buf = in_apk.read(f.filename)
|
||||
out_apk.writestr(f.filename, buf)
|
||||
os.remove(apkpath)
|
||||
p = SdkToolsPopen(['zipalign', '-v', '4', apkwithnewsig, apkpath])
|
||||
if p.returncode != 0:
|
||||
raise BuildException("Failed to align application")
|
||||
|
||||
|
||||
def apk_extract_signatures(apkpath, outdir, manifest=True):
|
||||
"""Extracts a signature files from APK and puts them into target directory.
|
||||
|
||||
|
@ -390,6 +390,17 @@ class CommonTest(unittest.TestCase):
|
||||
self.assertEqual(keytoolcertfingerprint,
|
||||
fdroidserver.common.apk_signer_fingerprint(apkfile))
|
||||
|
||||
def test_apk_signer_fingerprint_short(self):
|
||||
|
||||
# fingerprints fetched with: keytool -printcert -file ____.RSA
|
||||
testapks = (('repo/obb.main.oldversion_1444412523.apk', '818e469'),
|
||||
('repo/obb.main.twoversions_1101613.apk', '32a2362'),
|
||||
('repo/obb.main.twoversions_1101617.apk', '32a2362'))
|
||||
|
||||
for apkfile, keytoolcertfingerprint in testapks:
|
||||
self.assertEqual(keytoolcertfingerprint,
|
||||
fdroidserver.common.apk_signer_fingerprint_short(apkfile))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = optparse.OptionParser()
|
||||
|
Loading…
Reference in New Issue
Block a user