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 one certificate and ignores the other certificates in the chain.

The correct value comes from:

    apksigner verify --print-certs com.microsoft.emmx_242009005.apk | grep SHA-256

X.509 certificates are machine generated and just data, so are not
copyrightable.  So I included MSFTSIG.RSA directly.
This commit is contained in:
Hans-Christoph Steiner 2024-04-19 12:49:02 +02:00
parent 7d9cbf2f70
commit 1445eb4b21
3 changed files with 30 additions and 1 deletions

View File

@ -3886,6 +3886,16 @@ def get_cert_fingerprint(pubkey):
def get_certificate(signature_block_file):
"""Extract a DER certificate from JAR Signature's "Signature Block File".
Rarely, there is a certificate chain like in TLS (Microsoft does
this). In that case, this returns only the one certificate which
apksigner returns. This will be the same certificate that will be
printed by:
apksigner verify --print-certs com.microsoft.emmx.apk
This could be replaced by androguard's APK.get_certificate_der()
provided the cert chain fix was merged there. Maybe in 4.1.2?
Parameters
----------
signature_block_file
@ -3905,7 +3915,7 @@ def get_certificate(signature_block_file):
asn1Spec=rfc2315.SignedData())[0]
try:
certificates = content.getComponentByName('certificates')
cert = certificates[0].getComponentByName('certificate')
cert = certificates[-1].getComponentByName('certificate')
except PyAsn1Error:
logging.error("Certificates not found.")
return None

BIN
tests/MSFTSIG.RSA Normal file

Binary file not shown.

View File

@ -615,6 +615,25 @@ class CommonTest(unittest.TestCase):
self.assertFalse(fdroidserver.common.verify_apk_signature(twosigapk))
self.assertIsNone(fdroidserver.common.verify_apks(sourceapk, twosigapk, self.tmpdir))
def test_get_certificate_with_chain(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 one certificate in the
chain and ignores the rest.
The correct value comes from:
apksigner verify --print-certs com.microsoft.emmx_242009005.apk | grep SHA-256
"""
with open('MSFTSIG.RSA', 'rb') as fp:
cert = fdroidserver.common.get_certificate(fp.read())
self.assertEqual(
'01e1999710a82c2749b4d50c445dc85d670b6136089d0a766a73827c82a1eac9',
fdroidserver.common.signer_fingerprint(cert),
)
def test_write_to_config(self):
with tempfile.TemporaryDirectory() as tmpPath:
cfgPath = os.path.join(tmpPath, 'config.py')