Skip to content

Commit

Permalink
apkutils: Add certificate tests, different checksum algorithms
Browse files Browse the repository at this point in the history
This patch adds support for multiple checksum algorithms rather than
just returning md5. APK.get_certs() behaves the same way, but it can now
take an optional 'digestalgo' parameter. Digest algorithms are cached
per algorithm.

In the process of testing this, I also found that the library can't
handle some apps that add data files to META-INF (i.e. any kotlin app
will do this) because they try to interpret said data files as
certificates. The patch instead uses the AOSP guessing logic to get the
certificate name (must be in META-INF and be named *.RSA or *.DSA).
There's also a sample kotlin app and test case.
  • Loading branch information
defer committed Apr 16, 2019
1 parent c39b2d3 commit 83da02a
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 14 deletions.
18 changes: 9 additions & 9 deletions apkutils/__init__.py
Expand Up @@ -26,7 +26,7 @@ def __init__(self, apk_path):
self.strings = None
self.org_strings = None
self.opcodes = None
self.certs = []
self.certs = {}
self.arsc = None
self.strings_refx = None
self.app_icon = None
Expand Down Expand Up @@ -417,22 +417,22 @@ def get_arsc(self):

return self.arsc

def get_certs(self):
if not self.certs:
self._init_certs()
return self.certs
def get_certs(self, digestalgo='md5'):
if digestalgo not in self.certs:
self._init_certs(digestalgo)
return self.certs[digestalgo]

def _init_certs(self):
def _init_certs(self, digestalgo):
try:
with apkfile.ZipFile(self.apk_path, mode="r") as zf:
for name in zf.namelist():
if 'META-INF' in name:
if name.startswith('META-INF/') and name.endswith(('.DSA', '.RSA')):
data = zf.read(name)
mine = Magic(data).get_type()
if mine != 'txt':
from apkutils.cert import Certificate
cert = Certificate(data)
self.certs = cert.get()
cert = Certificate(data, digestalgo=digestalgo)
self.certs[digestalgo] = cert.get()
except Exception as e:
raise e

Expand Down
10 changes: 5 additions & 5 deletions apkutils/cert.py
Expand Up @@ -5,14 +5,14 @@

class Certificate:

def __init__(self, buff):
def __init__(self, buff, digestalgo='md5'):
self.content = []
self._parse(buff)
self._parse(buff, digestalgo)

def get(self):
return self.content

def _parse(self, buff):
def _parse(self, buff, digestalgo):
pkcs7 = crypto.load_pkcs7_data(crypto.FILETYPE_ASN1, buff)

certs_stack = _ffi.NULL
Expand All @@ -33,6 +33,6 @@ def _parse(self, buff):

for cert in pycerts:
name = str(cert.get_subject())[19:-2].replace('/', ', ')
md5 = cert.digest('md5').decode().replace(':', '')
checksum = cert.digest(digestalgo).decode().replace(':', '')

self.content.append((name, md5))
self.content.append((name, checksum))
Binary file added data/kotlin-app.zip
Binary file not shown.
37 changes: 37 additions & 0 deletions tests/test_certs.py
@@ -0,0 +1,37 @@
import os
import unittest

from apkutils import APK


class TestCerts(unittest.TestCase):

def test_youtube(self):
file_path = os.path.abspath(os.path.join(
os.path.dirname(__file__), "..", 'data', 'youtube.zip'))
apk = APK(file_path)

# Check that the default (md5) is correct
self.assertEqual([('C=US, ST=CA, L=Mountain View, O=Google, Inc, OU=Google, Inc, CN=Unknown',
'D046FC5D1FC3CD0E57C5444097CD5449')], apk.get_certs())

# Check that sha1 is correct
self.assertEqual([('C=US, ST=CA, L=Mountain View, O=Google, Inc, OU=Google, Inc, CN=Unknown',
'24BB24C05E47E0AEFA68A58A766179D9B613A600')], apk.get_certs('sha1'))

# Check that sha256 is correct
self.assertEqual([('C=US, ST=CA, L=Mountain View, O=Google, Inc, OU=Google, Inc, CN=Unknown',
'3D7A1223019AA39D9EA0E3436AB7C0896BFB4FB679F4DE5FE7C23F326C8F994A')], apk.get_certs('sha256'))


# Kotlin apps add data mime-typed files to META-INF which might
# confuse the certifiacte guesser, check that we can handle those
def test_kotlin_app(self):
file_path = os.path.abspath(os.path.join(
os.path.dirname(__file__), "..", 'data', 'kotlin-app.zip'))
apk = APK(file_path)
self.assertEqual([('CN=Android Debug, O=Android, C=US',
'299D8DE477962C781714EAAB76A90C287BB67123CD2909DE0F743838CAD264E4')], apk.get_certs('sha256'))

if __name__ == "__main__":
unittest.main()

0 comments on commit 83da02a

Please sign in to comment.