Skip to content

Commit

Permalink
APKv1 signatures with a cert chain are parsed like apksigner
Browse files Browse the repository at this point in the history
Microsoft signs their APKs with a X.509 certificate chain of trust, so
there are actually three certificates included. apksigner only cares
about the certificate that actually signs the manifest and ignores the
certificates that just sign other certificates.

https://apkpure.com/microsoft-edge-preview/com.microsoft.emmx/download
https://gitlab.com/fdroid/fdroidserver/-/issues/1128
https://gitlab.com/fdroid/fdroidserver/-/merge_requests/1466

X.509 certificates and JAR signatures are a combination of machine-
generated and plain data with trivial human input, so are not
copyrightable.  So I included signature files directly.
  • Loading branch information
eighthave committed Apr 19, 2024
1 parent 72e29ad commit 2cf8155
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 3 deletions.
13 changes: 10 additions & 3 deletions androguard/core/apk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1446,16 +1446,23 @@ def is_androidtv(self):
return self.get_attribute_value('uses-feature', 'name', required="false", name="android.hardware.touchscreen") == "android.hardware.touchscreen"

def get_certificate_der(self, filename):
"""
Return the DER coded X.509 certificate from the signature file.
"""Return the DER coded X.509 certificate from the signature file.
Rarely, there is a certificate chain like in TLS (Microsoft
does this). In that case, this returns only the final
certificate, which is the one used to sign the manifest. This
will be the same certificate that will be printed by:
apksigner verify --print-certs com.microsoft.emmx.apk
:param filename: Signature filename in APK
:returns: DER coded X.509 certificate as binary
"""
pkcs7message = self.get_file(filename)

pkcs7obj = cms.ContentInfo.load(pkcs7message)
cert = pkcs7obj['content']['certificates'][0].chosen.dump()
cert = pkcs7obj['content']['certificates'][-1].chosen.dump()
return cert

def get_certificate(self, filename):
Expand Down
Binary file added tests/data/APK/CertChain.apk
Binary file not shown.
22 changes: 22 additions & 0 deletions tests/test_apk.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,28 @@ def testAPKCert(self):

self.assertEqual(binascii.hexlify(cert).decode("ascii").upper(), expected)

def testAPKCertChain(self):
"""Test that APK signatures with a cert chain are parsed like apksigner.
Microsoft signs their APKs with a X.509 certificate chain of
trust, so there are actually three certificates
included. apksigner only cares about the certificate that
actually signs the manifest and ignores the certificates that
just sign other certificates.
The correct value comes from:
apksigner verify --print-certs com.microsoft.emmx_242009005.apk | grep SHA-256
"""
a = APK(os.path.join(test_dir, 'data/APK/CertChain.apk'), skip_analysis=True)
cert_der = a.get_certificate_der(a.get_signature_name())
sha256 = hashlib.sha256()
sha256.update(cert_der)
self.assertEqual(
sha256.hexdigest(),
'01e1999710a82c2749b4d50c445dc85d670b6136089d0a766a73827c82a1eac9',
)

def testAPKCertFingerprint(self):
"""
Test if certificates are correctly unpacked from the SignatureBlock files
Expand Down

0 comments on commit 2cf8155

Please sign in to comment.