def testAPKv2Signature(self): from androguard.core.bytecodes.apk import APK a = APK("examples/signing/TestActivity_signed_both.apk") self.assertTrue(a.is_signed_v1()) self.assertTrue(a.is_signed_v2()) self.assertTrue(a.is_signed()) # Signing name is maximal 8 chars... self.assertEqual(a.get_signature_name(), "META-INF/ANDROGUA.RSA") self.assertEqual(len(a.get_certificates_der_v2()), 1) # As we signed with the same certificate, both methods should return the # same content self.assertEqual(a.get_certificate_der(a.get_signature_name()), a.get_certificates_der_v2()[0]) from asn1crypto import x509 self.assertIsInstance(a.get_certificates_v2()[0], x509.Certificate) # Test if the certificate is also the same as on disk with open("examples/signing/certificate.der", "rb") as f: cert = f.read() cert_der_v1 = a.get_certificate_der(a.get_signature_name()) cert_der_v2 = a.get_certificates_der_v2()[0] for fun in [hashlib.md5, hashlib.sha1, hashlib.sha256, hashlib.sha512]: h1 = fun(cert).hexdigest() h2 = fun(cert_der_v1).hexdigest() h3 = fun(cert_der_v2).hexdigest() self.assertEqual(h1, h2) self.assertEqual(h1, h3) self.assertEqual(h2, h3)
def testAPKCertFingerprint(self): """ Test if certificates are correctly unpacked from the SignatureBlock files Check if fingerprints matches :return: """ from androguard.core.bytecodes.apk import APK import binascii from hashlib import md5, sha1, sha256 a = APK("examples/android/TestsAndroguard/bin/TestActivity.apk", skip_analysis=True) # this one is not signed v2, it is v1 only self.assertTrue(a.is_signed_v1()) self.assertFalse(a.is_signed_v2()) self.assertTrue(a.is_signed()) self.assertEqual(a.get_certificates_der_v2(), []) self.assertEqual(a.get_certificates_v2(), []) self.assertEqual(a.get_signature_name(), "META-INF/CERT.RSA") self.assertEqual(a.get_signature_names(), ["META-INF/CERT.RSA"]) cert = a.get_certificate(a.get_signature_name()) cert_der = a.get_certificate_der(a.get_signature_name()) # Keytool are the hashes collected by keytool -printcert -file CERT.RSA for h2, keytool in [(md5, "99:FF:FC:37:D3:64:87:DD:BA:AB:F1:7F:94:59:89:B5"), (sha1, "1E:0B:E4:01:F9:34:60:E0:8D:89:A3:EF:6E:27:25:55:6B:E1:D1:6B"), (sha256, "6F:5C:31:60:8F:1F:9E:28:5E:B6:34:3C:7C:8A:F0:7D:E8:1C:1F:B2:14:8B:53:49:BE:C9:06:44:41:44:57:6D")]: x = h2() x.update(cert_der) hash_hashlib = x.hexdigest() self.assertEqual(hash_hashlib.lower(), keytool.replace(":", "").lower())
def androsign_main(args_apk, args_hash, args_all, show): from androguard.core.bytecodes.apk import APK from androguard.util import get_certificate_name_string import hashlib import traceback from colorama import Fore, Style from asn1crypto import x509 # Keep the list of hash functions in sync with cli/entry_points.py:sign hashfunctions = dict(md5=hashlib.md5, sha1=hashlib.sha1, sha256=hashlib.sha256, sha512=hashlib.sha512, ) if args_hash.lower() not in hashfunctions: print("Hash function {} not supported!" .format(args_hash.lower()), file=sys.stderr) print("Use one of {}" .format(", ".join(hashfunctions.keys())), file=sys.stderr) sys.exit(1) for path in args_apk: try: a = APK(path) print("{}, package: '{}'".format(os.path.basename(path), a.get_package())) print("Is signed v1: {}".format(a.is_signed_v1())) print("Is signed v2: {}".format(a.is_signed_v2())) certs = set(a.get_certificates_der_v2() + [a.get_certificate_der(x) for x in a.get_signature_names()]) if len(certs) > 0: print("Found {} unique certificates".format(len(certs))) for cert in certs: if show: x509_cert = x509.Certificate.load(cert) print("Issuer:", get_certificate_name_string(x509_cert.issuer, short=True)) print("Subject:", get_certificate_name_string(x509_cert.subject, short=True)) print("Serial Number:", hex(x509_cert.serial_number)) print("Hash Algorithm:", x509_cert.hash_algo) print("Signature Algorithm:", x509_cert.signature_algo) print("Valid not before:", x509_cert['tbs_certificate']['validity']['not_before'].native) print("Valid not after:", x509_cert['tbs_certificate']['validity']['not_after'].native) if not args_all: print("{} {}".format(args_hash.lower(), hashfunctions[args_hash.lower()](cert).hexdigest())) else: for k, v in hashfunctions.items(): print("{} {}".format(k, v(cert).hexdigest())) print() except: print(Fore.RED + "Error in {}".format(os.path.basename(path)) + Style.RESET_ALL, file=sys.stderr) traceback.print_exc(file=sys.stderr) if len(args_apk) > 1: print()
def main(): parser = get_parser() args = parser.parse_args() hashfunctions = dict(md5=hashlib.md5, sha1=hashlib.sha1, sha256=hashlib.sha256, sha512=hashlib.sha512, ) if args.hash.lower() not in hashfunctions: print("Hash function {} not supported!".format(args.hash.lower()), file=sys.stderr) print("Use one of {}".format(", ".join(hashfunctions.keys())), file=sys.stderr) sys.exit(1) for path in args.apk: try: a = APK(path) print("{}, package: '{}'".format(os.path.basename(path), a.get_package())) print("Is signed v1: {}".format(a.is_signed_v1())) print("Is signed v2: {}".format(a.is_signed_v2())) certs = set(a.get_certificates_der_v2() + [a.get_certificate_der(x) for x in a.get_signature_names()]) if len(certs) > 0: print("Found {} unique certificates".format(len(certs))) for cert in certs: if args.show: x509_cert = x509.Certificate.load(cert) print("Issuer:", get_certificate_name_string(x509_cert.issuer, short=True)) print("Subject:", get_certificate_name_string(x509_cert.subject, short=True)) print("Serial Number:", hex(x509_cert.serial_number)) print("Hash Algorithm:", x509_cert.hash_algo) print("Signature Algorithm:", x509_cert.signature_algo) print("Valid not before:", x509_cert['tbs_certificate']['validity']['not_before'].native) print("Valid not after:", x509_cert['tbs_certificate']['validity']['not_after'].native) if not args.all: print("{} {}".format(args.hash.lower(), hashfunctions[args.hash.lower()](cert).hexdigest())) else: for k, v in hashfunctions.items(): print("{} {}".format(k, v(cert).hexdigest())) print() except: print(Fore.RED + "Error in {}".format(os.path.basename(path)) + Style.RESET_ALL, file=sys.stderr) traceback.print_exc(file=sys.stderr) if len(args.apk) > 1: print()
def androguard_certinfo(app_dir, app_file): """Return certificate information.""" certlist = [] apk_file = os.path.join(app_dir, app_file) hashfunctions = dict( md5=hashlib.md5, sha1=hashlib.sha1, sha256=hashlib.sha256, sha512=hashlib.sha512, ) a = APK(apk_file) certlist.append("v1: {}".format(a.is_signed_v1())) certlist.append("v2: {}".format(a.is_signed_v2())) certlist.append("v3: {}".format(a.is_signed_v3())) certs = set(a.get_certificates_der_v3() + a.get_certificates_der_v2() + [a.get_certificate_der(x) for x in a.get_signature_names()]) pkeys = set(a.get_public_keys_der_v3() + a.get_public_keys_der_v2()) for cert in certs: x509_cert = x509.Certificate.load(cert) certlist.append("Subject: {}".format( get_certificate_name_string(x509_cert.subject, short=True))) certlist.append("Signature Algorithm: {}".format( x509_cert.signature_algo)) certlist.append("Valid From: {}".format( x509_cert['tbs_certificate']['validity']['not_before'].native)) certlist.append("Valid To: {}".format( x509_cert['tbs_certificate']['validity']['not_after'].native)) certlist.append("Issuer: {}".format( get_certificate_name_string(x509_cert.issuer, short=True))) certlist.append("Serial Number: {}".format(hex( x509_cert.serial_number))) certlist.append("Hash Algorithm: {}".format(x509_cert.hash_algo)) for k, v in hashfunctions.items(): certlist.append("{} {}".format(k, v(cert).hexdigest())) for public_key in pkeys: x509_public_key = keys.PublicKeyInfo.load(public_key) certlist.append("PublicKey Algorithm: {}".format( x509_public_key.algorithm)) certlist.append("Bit Size: {}".format(x509_public_key.bit_size)) certlist.append("Fingerprint: {}".format( binascii.hexlify(x509_public_key.fingerprint).decode("utf-8"))) try: certlist.append("Hash Algorithm: {}".format( x509_public_key.hash_algo)) except ValueError as ve: # RSA pkey does not have an hash algorithm pass return '\n'.join(certlist)
def extract_attributes(sha256): with NamedTemporaryFile() as f: f.write(default_storage.open(sha256).read()) f.seek(0) sign = ApplicationSignature.compute_from_apk(f.name) package = sign.handle sign = sign.to_dict() a = APK(f.name) sign['uploaded_at'] = datetime.now() sign['sha256'] = sha256 sign['activities'] = a.get_activities() sign['features'] = a.get_features() sign['libraries'] = a.get_libraries() sign['main_activity'] = a.get_activities() sign['min_sdk_version'] = a.get_min_sdk_version() sign['max_sdk_version'] = a.get_max_sdk_version() sign['target_sdk_version'] = a.get_target_sdk_version() sign['permissions'] = a.get_permissions() sign['aosp_permissions'] = a.get_requested_aosp_permissions() sign[ 'third_party_permissions'] = a.get_requested_third_party_permissions( ) sign['providers'] = a.get_providers() sign['receivers'] = a.get_receivers() sign['services'] = a.get_services() sign['is_valid'] = a.is_valid_APK() sign['is_signed'] = a.is_signed() sign['is_signed_v1'] = a.is_signed_v1() sign['is_signed_v2'] = a.is_signed_v2() sign['is_signed_v3'] = a.is_signed_v3() if not es.exists(settings.ELASTICSEARCH_APK_INDEX, id=sha256): es.index(index=settings.ELASTICSEARCH_APK_INDEX, id=sha256, body=sign) else: es.update(index=settings.ELASTICSEARCH_APK_INDEX, id=sha256, body={'doc': sign}, retry_on_conflict=5) del a, sign, f gc.collect() return package
def main(): parser = get_parser() args = parser.parse_args() hashfunctions = dict(md5=hashlib.md5, sha1=hashlib.sha1, sha256=hashlib.sha256, sha512=hashlib.sha512, ) if args.hash.lower() not in hashfunctions: print("Hash function {} not supported!".format(args.hash.lower()), file=sys.stderr) print("Use one of {}".format(", ".join(hashfunctions.keys())), file=sys.stderr) sys.exit(1) for path in args.apk: try: a = APK(path) print("{}, package: '{}'".format(os.path.basename(path), a.get_package())) print("Is signed v1: {}".format(a.is_signed_v1())) print("Is signed v2: {}".format(a.is_signed_v2())) certs = set(a.get_certificates_der_v2() + [a.get_certificate_der(x) for x in a.get_signature_names()]) if len(certs) > 0: print("Found {} unique certificates".format(len(certs))) for cert in certs: if not args.all: print("{} {}".format(args.hash.lower(), hashfunctions[args.hash.lower()](cert).hexdigest())) else: for k, v in hashfunctions.items(): print("{} {}".format(k, v(cert).hexdigest())) except: print(Fore.RED + "Error in {}".format(os.path.basename(path)) + Style.RESET_ALL, file=sys.stderr) traceback.print_exc(file=sys.stderr) if len(args.apk) > 1: print()
def cert_info(app_dir, app_file): """Return certificate information.""" try: logger.info('Reading Code Signing Certificate') manifestfile = None manidat = '' cert_info = '' certlist = [] cert_path = os.path.join(app_dir, 'META-INF/') apk_file = os.path.join(app_dir, app_file) hashfunctions = { 'md5': hashlib.md5, 'sha1': hashlib.sha1, 'sha256': hashlib.sha256, 'sha512': hashlib.sha512, } files = [ f for f in os.listdir(cert_path) if os.path.isfile(os.path.join(cert_path, f)) ] a = APK(apk_file) if a.is_signed(): certlist.append('APK is signed') else: certlist.append('Missing certificate') certlist.append('v1 signature: {}'.format(a.is_signed_v1())) certlist.append('v2 signature: {}'.format(a.is_signed_v2())) certlist.append('v3 signature: {}'.format(a.is_signed_v3())) certs = set( a.get_certificates_der_v3() + a.get_certificates_der_v2() + [a.get_certificate_der(x) for x in a.get_signature_names()]) pkeys = set(a.get_public_keys_der_v3() + a.get_public_keys_der_v2()) if len(certs) > 0: certlist.append('Found {} unique certificates'.format(len(certs))) for cert in certs: x509_cert = x509.Certificate.load(cert) certlist.append('Subject: {}'.format( get_certificate_name_string(x509_cert.subject, short=True))) certlist.append('Signature Algorithm: {}'.format( x509_cert.signature_algo)) certlist.append('Valid From: {}'.format( x509_cert['tbs_certificate']['validity']['not_before'].native)) certlist.append('Valid To: {}'.format( x509_cert['tbs_certificate']['validity']['not_after'].native)) certlist.append('Issuer: {}'.format( get_certificate_name_string(x509_cert.issuer, short=True))) certlist.append('Serial Number: {}'.format( hex(x509_cert.serial_number))) certlist.append('Hash Algorithm: {}'.format(x509_cert.hash_algo)) for k, v in hashfunctions.items(): certlist.append('{}: {}'.format(k, v(cert).hexdigest())) for public_key in pkeys: x509_public_key = asymmetric.load_public_key(public_key) certlist.append('PublicKey Algorithm: {}'.format( x509_public_key.algorithm)) certlist.append('Bit Size: {}'.format(x509_public_key.bit_size)) certlist.append('Fingerprint: {}'.format( binascii.hexlify(x509_public_key.fingerprint).decode('utf-8'))) cert_info = '\n'.join(certlist) if 'MANIFEST.MF' in files: manifestfile = os.path.join(cert_path, 'MANIFEST.MF') if manifestfile: with open(manifestfile, 'r', encoding='utf-8') as manifile: manidat = manifile.read() sha256_digest = bool(re.findall(r'SHA-256-Digest', manidat)) findings = [] if a.is_signed(): findings.append(('good', 'Application is signed with a code ' 'signing certificate')) else: findings.append(('bad', 'Code signing certificate not found')) if a.is_signed_v1(): status = 'bad' if a.is_signed_v2() or a.is_signed_v3(): status = 'warning' findings.append( (status, 'Application is signed with v1 signature scheme, ' 'making it vulnerable to Janus vulnerability on ' 'Android <7.0')) if re.findall(r'CN=Android Debug', cert_info): findings.append( ('bad', 'Application signed with a debug certificate. ' 'Production application must not be shipped ' 'with a debug certificate.')) if re.findall(r'Hash Algorithm: sha1', cert_info): status = 'bad' desc = ('Application is signed with SHA1withRSA. ' 'SHA1 hash algorithm is known to have ' 'collision issues.') if sha256_digest: status = 'warning' desc += (' The manifest file indicates SHA256withRSA' ' is in use.') findings.append((status, desc)) cert_dic = { 'certificate_info': cert_info, 'certificate_findings': findings, } return cert_dic except Exception: logger.exception('Reading Code Signing Certificate') return {}
def androsign_main(args_apk, args_hash, args_all, show): from androguard.core.bytecodes.apk import APK from androguard.util import get_certificate_name_string import hashlib import binascii import traceback from colorama import Fore, Style from asn1crypto import x509, keys from oscrypto import asymmetric # Keep the list of hash functions in sync with cli/entry_points.py:sign hashfunctions = dict(md5=hashlib.md5, sha1=hashlib.sha1, sha256=hashlib.sha256, sha512=hashlib.sha512, ) if args_hash.lower() not in hashfunctions: print("Hash function {} not supported!" .format(args_hash.lower()), file=sys.stderr) print("Use one of {}" .format(", ".join(hashfunctions.keys())), file=sys.stderr) sys.exit(1) for path in args_apk: try: a = APK(path) print("{}, package: '{}'".format(os.path.basename(path), a.get_package())) print("Is signed v1: {}".format(a.is_signed_v1())) print("Is signed v2: {}".format(a.is_signed_v2())) print("Is signed v3: {}".format(a.is_signed_v3())) certs = set(a.get_certificates_der_v3() + a.get_certificates_der_v2() + [a.get_certificate_der(x) for x in a.get_signature_names()]) pkeys = set(a.get_public_keys_der_v3() + a.get_public_keys_der_v2()) if len(certs) > 0: print("Found {} unique certificates".format(len(certs))) for cert in certs: if show: x509_cert = x509.Certificate.load(cert) print("Issuer:", get_certificate_name_string(x509_cert.issuer, short=True)) print("Subject:", get_certificate_name_string(x509_cert.subject, short=True)) print("Serial Number:", hex(x509_cert.serial_number)) print("Hash Algorithm:", x509_cert.hash_algo) print("Signature Algorithm:", x509_cert.signature_algo) print("Valid not before:", x509_cert['tbs_certificate']['validity']['not_before'].native) print("Valid not after:", x509_cert['tbs_certificate']['validity']['not_after'].native) if not args_all: print("{} {}".format(args_hash.lower(), hashfunctions[args_hash.lower()](cert).hexdigest())) else: for k, v in hashfunctions.items(): print("{} {}".format(k, v(cert).hexdigest())) print() if len(certs) > 0: print("Found {} unique public keys associated with the certs".format(len(pkeys))) for public_key in pkeys: if show: x509_public_key = asymmetric.load_public_key(public_key) print("PublicKey Algorithm:", x509_public_key.algorithm) print("Bit Size:", x509_public_key.bit_size) print("Fingerprint:", binascii.hexlify(x509_public_key.fingerprint)) try: print("Hash Algorithm:", hash_algo(x509_public_key)) except ValueError as ve: # RSA pkey does not have an hash algorithm pass print() except: print(Fore.RED + "Error in {}".format(os.path.basename(path)) + Style.RESET_ALL, file=sys.stderr) traceback.print_exc(file=sys.stderr) if len(args_apk) > 1: print()
def main(): parser = get_parser() args = parser.parse_args() hashfunctions = dict( md5=hashlib.md5, sha1=hashlib.sha1, sha256=hashlib.sha256, sha512=hashlib.sha512, ) if args.hash.lower() not in hashfunctions: print("Hash function {} not supported!".format(args.hash.lower()), file=sys.stderr) print("Use one of {}".format(", ".join(hashfunctions.keys())), file=sys.stderr) sys.exit(1) for path in args.apk: try: a = APK(path) print("{}, package: '{}'".format(os.path.basename(path), a.get_package())) print("Is signed v1: {}".format(a.is_signed_v1())) print("Is signed v2: {}".format(a.is_signed_v2())) certs = set( a.get_certificates_der_v2() + [a.get_certificate_der(x) for x in a.get_signature_names()]) if len(certs) > 0: print("Found {} unique certificates".format(len(certs))) for cert in certs: if args.show: x509_cert = x509.Certificate.load(cert) print( "Issuer:", get_certificate_name_string(x509_cert.issuer, short=True)) print( "Subject:", get_certificate_name_string(x509_cert.subject, short=True)) print("Serial Number:", hex(x509_cert.serial_number)) print("Hash Algorithm:", x509_cert.hash_algo) print("Signature Algorithm:", x509_cert.signature_algo) print( "Valid not before:", x509_cert['tbs_certificate'] ['validity']['not_before'].native) print( "Valid not after:", x509_cert['tbs_certificate'] ['validity']['not_after'].native) if not args.all: print("{} {}".format( args.hash.lower(), hashfunctions[args.hash.lower()](cert).hexdigest())) else: for k, v in hashfunctions.items(): print("{} {}".format(k, v(cert).hexdigest())) print() except: print(Fore.RED + "Error in {}".format(os.path.basename(path)) + Style.RESET_ALL, file=sys.stderr) traceback.print_exc(file=sys.stderr) if len(args.apk) > 1: print()
def cert_info(app_dir, app_file): """Return certificate information.""" try: logger.info('Reading Code Signing Certificate') issued = '' manidat = '' certlist = [] cert_path = os.path.join(app_dir, 'META-INF/') apk_file = os.path.join(app_dir, app_file) hashfunctions = { 'md5': hashlib.md5, 'sha1': hashlib.sha1, 'sha256': hashlib.sha256, 'sha512': hashlib.sha512, } files = [ f for f in os.listdir(cert_path) if os.path.isfile(os.path.join(cert_path, f)) ] a = APK(apk_file) if a.is_signed(): certlist.append('APK is signed') else: certlist.append('Missing certificate') certlist.append('v1 signature: {}'.format(a.is_signed_v1())) certlist.append('v2 signature: {}'.format(a.is_signed_v2())) certlist.append('v3 signature: {}'.format(a.is_signed_v3())) certs = set( a.get_certificates_der_v3() + a.get_certificates_der_v2() + [a.get_certificate_der(x) for x in a.get_signature_names()]) pkeys = set(a.get_public_keys_der_v3() + a.get_public_keys_der_v2()) if len(certs) > 0: certlist.append('Found {} unique certificates'.format(len(certs))) for cert in certs: x509_cert = x509.Certificate.load(cert) certlist.append('Subject: {}'.format( get_certificate_name_string(x509_cert.subject, short=True))) certlist.append('Signature Algorithm: {}'.format( x509_cert.signature_algo)) certlist.append('Valid From: {}'.format( x509_cert['tbs_certificate']['validity']['not_before'].native)) certlist.append('Valid To: {}'.format( x509_cert['tbs_certificate']['validity']['not_after'].native)) certlist.append('Issuer: {}'.format( get_certificate_name_string(x509_cert.issuer, short=True))) certlist.append('Serial Number: {}'.format( hex(x509_cert.serial_number))) certlist.append('Hash Algorithm: {}'.format(x509_cert.hash_algo)) for k, v in hashfunctions.items(): certlist.append('{}: {}'.format(k, v(cert).hexdigest())) for public_key in pkeys: x509_public_key = keys.PublicKeyInfo.load(public_key) certlist.append('PublicKey Algorithm: {}'.format( x509_public_key.algorithm)) certlist.append('Bit Size: {}'.format(x509_public_key.bit_size)) certlist.append('Fingerprint: {}'.format( binascii.hexlify(x509_public_key.fingerprint).decode('utf-8'))) try: certlist.append('Hash Algorithm: {}'.format( x509_public_key.hash_algo)) except ValueError: pass certlist = '\n'.join(certlist) if a.is_signed(): issued = 'good' else: issued = 'missing' if re.findall(r'CN=Android Debug', certlist): issued = 'bad' if re.findall(r'Hash Algorithm: sha1', certlist): issued = 'bad hash' if 'MANIFEST.MF' in files: manifestfile = os.path.join(cert_path, 'MANIFEST.MF') if manifestfile: with open(manifestfile, 'r', encoding='utf-8') as manifile: manidat = manifile.read() sha256_digest = bool(re.findall(r'SHA-256-Digest', manidat)) cert_dic = { 'cert_info': certlist, 'issued': issued, 'sha256Digest': sha256_digest, } return cert_dic except Exception: logger.exception('Reading Code Signing Certificate')
def testApksignAPKs(self): # These APKs are from the apksign testcases and cover # all different signature algorithms as well as some error cases from androguard.core.bytecodes.apk import APK import zipfile from asn1crypto import x509, pem import binascii root = "examples/signing/apksig" # Correct values generated with openssl: certfp = { "dsa-1024.x509.pem": "fee7c19ff9bfb4197b3727b9fd92d95406b1bd96db99ea642f5faac019a389d7", "dsa-2048.x509.pem": "97cce0bab292c2d5afb9de90e1810b41a5d25c006a10d10982896aa12ab35a9e", "dsa-3072.x509.pem": "966a4537058d24098ea213f12d4b24e37ff5a1d8f68deb8a753374881f23e474", "ec-p256.x509.pem": "6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599", "ec-p384.x509.pem": "5e7777ada7ee7ce8f9c4d1b07094876e5604617b7988b4c5d5b764a23431afbe", "ec-p521.x509.pem": "69b50381d98bebcd27df6d7df8af8c8b38d0e51e9168a95ab992d1a9da6082da", "rsa-1024.x509.pem": "bc5e64eab1c4b5137c0fbc5ed05850b3a148d1c41775cffa4d96eea90bdd0eb8", "rsa-16384.x509.pem": "f3c6b37909f6df310652fbd7c55ec27d3079dcf695dc6e75e22ba7c4e1c95601", "rsa-2048.x509.pem": "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8", "rsa-3072.x509.pem": "483934461229a780010bc07cd6eeb0b67025fc4fe255757abbf5c3f2ed249e89", "rsa-4096.x509.pem": "6a46158f87753395a807edcc7640ac99c9125f6b6e025bdbf461ff281e64e685", "rsa-8192.x509.pem": "060d0a24fea9b60d857225873f78838e081795f7ef2d1ea401262bbd75a58234", } will_not_validate_correctly = [ "targetSandboxVersion-2.apk", "targetSandboxVersion-2.apk", "v1-only-with-cr-in-entry-name.apk", "v1-only-with-lf-in-entry-name.apk", "v1-only-with-nul-in-entry-name.apk", "v1-only-with-rsa-1024-cert-not-der2.apk", "v2-only-cert-and-public-key-mismatch.apk", "v2-only-with-dsa-sha256-1024-sig-does-not-verify.apk", ] # Collect possible hashes for certificates # Unfortunately, not all certificates are supplied... for apath in os.listdir(root): if apath in certfp: with open(os.path.join(root, apath), "rb") as fp: cert = x509.Certificate.load(pem.unarmor(fp.read())[2]) h = cert.sha256_fingerprint.replace(" ","").lower() self.assertEqual(h, certfp[apath]) self.assertIn(h, certfp.values()) for apath in os.listdir(root): if apath.endswith(".apk"): if apath == "v2-only-garbage-between-cd-and-eocd.apk" or \ apath == "v2-only-truncated-cd.apk": # Can not load as APK if sys.version_info.major == 2: # Different name in python2... with self.assertRaises(zipfile.BadZipfile): APK(os.path.join(root, apath)) else: with self.assertRaises(zipfile.BadZipFile): APK(os.path.join(root, apath)) continue elif apath in will_not_validate_correctly: # These APKs are faulty (by design) and will return a not correct fingerprint. # TODO: we need to check if we can prevent such errors... continue a = APK(os.path.join(root, apath)) self.assertIsInstance(a, APK) # Special error cases if apath == "v2-only-apk-sig-block-size-mismatch.apk": with self.assertRaises(AssertionError): a.is_signed_v2() continue elif apath == "v2-only-empty.apk": with self.assertRaises(AssertionError): a.is_signed_v2() continue if a.is_signed_v1(): if apath == "weird-compression-method.apk": with self.assertRaises(NotImplementedError): for c in a.get_signature_names(): a.get_certificate(c) elif apath == "v1-only-with-rsa-1024-cert-not-der.apk": for sig in a.get_signature_names(): c = a.get_certificate(sig) h = c.sha256_fingerprint.replace(" ","").lower() self.assertNotIn(h, certfp.values()) # print([apath, h]) # I do not know, why put this file? der = a.get_certificate_der(sig) apk.show_Certificate(c, True) apk.show_Certificate(c, False) self.assertEqual(hashlib.sha256(der).hexdigest(), h) pass else: for sig in a.get_signature_names(): c = a.get_certificate(sig) h = c.sha256_fingerprint.replace(" ","").lower() self.assertIn(h, certfp.values()) # Check that we get the same signature if we take the DER der = a.get_certificate_der(sig) self.assertEqual(hashlib.sha256(der).hexdigest(), h) if a.is_signed_v2(): if apath == "weird-compression-method.apk": with self.assertRaises(NotImplementedError): a.get_certificates_der_v2() elif apath == "v2-only-with-rsa-pkcs1-sha256-1024-cert-not-der.apk": # FIXME # Not sure what this one should do... but the certificate fingerprint is weird # as the hash over the DER is not the same when using the certificate continue else: for c in a.get_certificates_der_v2(): cert = x509.Certificate.load(c) h = cert.sha256_fingerprint.replace(" ","").lower() self.assertIn(h, certfp.values()) # Check that we get the same signature if we take the DER self.assertEqual(hashlib.sha256(c).hexdigest(), h)
def testApksignAPKs(self): # These APKs are from the apksign testcases and cover # all different signature algorithms as well as some error cases from androguard.core.bytecodes.apk import APK import zipfile from asn1crypto import x509, pem import binascii root = "examples/signing/apksig" # Correct values generated with openssl: certfp = { "dsa-1024.x509.pem": "fee7c19ff9bfb4197b3727b9fd92d95406b1bd96db99ea642f5faac019a389d7", "dsa-2048.x509.pem": "97cce0bab292c2d5afb9de90e1810b41a5d25c006a10d10982896aa12ab35a9e", "dsa-3072.x509.pem": "966a4537058d24098ea213f12d4b24e37ff5a1d8f68deb8a753374881f23e474", "ec-p256.x509.pem": "6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599", "ec-p384.x509.pem": "5e7777ada7ee7ce8f9c4d1b07094876e5604617b7988b4c5d5b764a23431afbe", "ec-p521.x509.pem": "69b50381d98bebcd27df6d7df8af8c8b38d0e51e9168a95ab992d1a9da6082da", "rsa-1024.x509.pem": "bc5e64eab1c4b5137c0fbc5ed05850b3a148d1c41775cffa4d96eea90bdd0eb8", "rsa-16384.x509.pem": "f3c6b37909f6df310652fbd7c55ec27d3079dcf695dc6e75e22ba7c4e1c95601", "rsa-2048.x509.pem": "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8", "rsa-3072.x509.pem": "483934461229a780010bc07cd6eeb0b67025fc4fe255757abbf5c3f2ed249e89", "rsa-4096.x509.pem": "6a46158f87753395a807edcc7640ac99c9125f6b6e025bdbf461ff281e64e685", "rsa-8192.x509.pem": "060d0a24fea9b60d857225873f78838e081795f7ef2d1ea401262bbd75a58234", } will_not_validate_correctly = [ "targetSandboxVersion-2.apk", "targetSandboxVersion-2.apk", "v1-only-with-cr-in-entry-name.apk", "v1-only-with-lf-in-entry-name.apk", "v1-only-with-nul-in-entry-name.apk", "v1-only-with-rsa-1024-cert-not-der2.apk", "v2-only-cert-and-public-key-mismatch.apk", "v2-only-with-dsa-sha256-1024-sig-does-not-verify.apk", ] # Collect possible hashes for certificates # Unfortunately, not all certificates are supplied... for apath in os.listdir(root): if apath in certfp: with open(os.path.join(root, apath), "rb") as fp: cert = x509.Certificate.load(pem.unarmor(fp.read())[2]) h = cert.sha256_fingerprint.replace(" ", "").lower() self.assertEqual(h, certfp[apath]) self.assertIn(h, certfp.values()) for apath in os.listdir(root): if apath.endswith(".apk"): if apath == "v2-only-garbage-between-cd-and-eocd.apk" or \ apath == "v2-only-truncated-cd.apk": # Can not load as APK if sys.version_info.major == 2: # Different name in python2... with self.assertRaises(zipfile.BadZipfile): APK(os.path.join(root, apath)) else: with self.assertRaises(zipfile.BadZipFile): APK(os.path.join(root, apath)) continue elif apath in will_not_validate_correctly: # These APKs are faulty (by design) and will return a not correct fingerprint. # TODO: we need to check if we can prevent such errors... continue a = APK(os.path.join(root, apath)) self.assertIsInstance(a, APK) # Special error cases if apath == "v2-only-apk-sig-block-size-mismatch.apk": with self.assertRaises(AssertionError): a.is_signed_v2() continue elif apath == "v2-only-empty.apk": with self.assertRaises(AssertionError): a.is_signed_v2() continue if a.is_signed_v1(): if apath == "weird-compression-method.apk": with self.assertRaises(NotImplementedError): for c in a.get_signature_names(): a.get_certificate(c) elif apath == "v1-only-with-rsa-1024-cert-not-der.apk": for sig in a.get_signature_names(): c = a.get_certificate(sig) h = c.sha256_fingerprint.replace(" ", "").lower() self.assertNotIn(h, certfp.values()) # print([apath, h]) # I do not know, why put this file? der = a.get_certificate_der(sig) apk.show_Certificate(c, True) apk.show_Certificate(c, False) self.assertEqual( hashlib.sha256(der).hexdigest(), h) pass else: for sig in a.get_signature_names(): c = a.get_certificate(sig) h = c.sha256_fingerprint.replace(" ", "").lower() self.assertIn(h, certfp.values()) # Check that we get the same signature if we take the DER der = a.get_certificate_der(sig) self.assertEqual( hashlib.sha256(der).hexdigest(), h) if a.is_signed_v2(): if apath == "weird-compression-method.apk": with self.assertRaises(NotImplementedError): a.get_certificates_der_v2() elif apath == "v2-only-with-rsa-pkcs1-sha256-1024-cert-not-der.apk": # FIXME # Not sure what this one should do... but the certificate fingerprint is weird # as the hash over the DER is not the same when using the certificate continue else: for c in a.get_certificates_der_v2(): cert = x509.Certificate.load(c) h = cert.sha256_fingerprint.replace(" ", "").lower() self.assertIn(h, certfp.values()) # Check that we get the same signature if we take the DER self.assertEqual(hashlib.sha256(c).hexdigest(), h)
def testApksignAPKs(self): # These APKs are from the apksign testcases and cover # all different signature algorithms as well as some error cases from androguard.core.bytecodes.apk import APK import zipfile from asn1crypto import x509, pem import binascii root = "examples/signing/apksig" # Correct values generated with openssl: # In the apksig repo:src/test/resources/com/android/apksig # for f in *.pem; do openssl x509 -in $f -noout -sha256 -fingerprint; done certfp = { 'dsa-1024.x509.pem': 'fee7c19ff9bfb4197b3727b9fd92d95406b1bd96db99ea642f5faac019a389d7', 'dsa-2048.x509.pem': '97cce0bab292c2d5afb9de90e1810b41a5d25c006a10d10982896aa12ab35a9e', 'dsa-3072.x509.pem': '966a4537058d24098ea213f12d4b24e37ff5a1d8f68deb8a753374881f23e474', 'ec-p256.x509.pem': '6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599', 'ec-p384.x509.pem': '5e7777ada7ee7ce8f9c4d1b07094876e5604617b7988b4c5d5b764a23431afbe', 'ec-p521.x509.pem': '69b50381d98bebcd27df6d7df8af8c8b38d0e51e9168a95ab992d1a9da6082da', 'rsa-1024_2.x509.pem': 'eba3685e799f59804684abebf0363e14ccb1c213e2b954a22669714ed97f61e9', 'rsa-1024.x509.pem': 'bc5e64eab1c4b5137c0fbc5ed05850b3a148d1c41775cffa4d96eea90bdd0eb8', 'rsa-16384.x509.pem': 'f3c6b37909f6df310652fbd7c55ec27d3079dcf695dc6e75e22ba7c4e1c95601', 'rsa-2048_2.x509.pem': '681b0e56a796350c08647352a4db800cc44b2adc8f4c72fa350bd05d4d50264d', 'rsa-2048_3.x509.pem': 'bb77a72efc60e66501ab75953af735874f82cfe52a70d035186a01b3482180f3', 'rsa-2048.x509.pem': 'fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8', 'rsa-3072.x509.pem': '483934461229a780010bc07cd6eeb0b67025fc4fe255757abbf5c3f2ed249e89', 'rsa-4096.x509.pem': '6a46158f87753395a807edcc7640ac99c9125f6b6e025bdbf461ff281e64e685', 'rsa-8192.x509.pem': '060d0a24fea9b60d857225873f78838e081795f7ef2d1ea401262bbd75a58234', } will_not_validate_correctly = [ "targetSandboxVersion-2.apk", "targetSandboxVersion-2.apk", "v1-only-with-cr-in-entry-name.apk", "v1-only-with-lf-in-entry-name.apk", "v1-only-with-nul-in-entry-name.apk", "v1-only-with-rsa-1024-cert-not-der2.apk", "v2-only-cert-and-public-key-mismatch.apk", "v2-only-with-dsa-sha256-1024-sig-does-not-verify.apk", "debuggable-boolean.apk", "debuggable-resource.apk", ] # Collect possible hashes for certificates # Unfortunately, not all certificates are supplied... for apath in os.listdir(root): if apath in certfp: with open(os.path.join(root, apath), "rb") as fp: cert = x509.Certificate.load(pem.unarmor(fp.read())[2]) h = cert.sha256_fingerprint.replace(" ","").lower() self.assertEqual(h, certfp[apath]) self.assertIn(h, certfp.values()) for apath in os.listdir(root): if apath.endswith(".apk"): if apath == "v2-only-garbage-between-cd-and-eocd.apk" or \ apath == "v2-only-truncated-cd.apk" or \ apath == "v1v2v3-with-rsa-2048-lineage-3-signers-invalid-zip.apk": # Can not load as APK with self.assertRaises(zipfile.BadZipFile): APK(os.path.join(root, apath)) continue elif apath in will_not_validate_correctly: # These APKs are faulty (by design) and will return a not correct fingerprint. # TODO: we need to check if we can prevent such errors... continue a = APK(os.path.join(root, apath)) self.assertIsInstance(a, APK) # Test if the correct method returns True, while others return # False m_tests = {'1': a.is_signed_v1, '2': a.is_signed_v2, '3': a.is_signed_v3} # These APKs will raise an error excluded = [ "v1v2v3-with-rsa-2048-lineage-3-signers-no-sig-block.apk", "v2-only-apk-sig-block-size-mismatch.apk", "v2-only-empty.apk", "v2-only-wrong-apk-sig-block-magic.apk", "v2-stripped.apk", "v2-stripped-with-ignorable-signing-schemes.apk", "v2v3-signed-v3-block-stripped.apk", "v3-only-empty.apk", "v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", "v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk", "v3-stripped.apk", ] if apath[0] == "v" and apath not in excluded: methods = apath.split("-", 1)[0].split("v")[1:] for m, f in m_tests.items(): if m in methods: self.assertTrue(f()) else: self.assertFalse(f()) # Special error cases if apath == "v2-only-apk-sig-block-size-mismatch.apk": with self.assertRaises(apk.BrokenAPKError): a.is_signed_v2() continue elif apath == "v2-only-empty.apk": with self.assertRaises(apk.BrokenAPKError): a.is_signed_v2() continue elif apath == "v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk": with self.assertRaises(apk.BrokenAPKError): a.is_signed_v3() continue if a.is_signed_v1(): if apath == "weird-compression-method.apk": with self.assertRaises(NotImplementedError): for c in a.get_signature_names(): a.get_certificate(c) elif apath == "v1-only-with-rsa-1024-cert-not-der.apk": for sig in a.get_signature_names(): c = a.get_certificate(sig) h = c.sha256_fingerprint.replace(" ","").lower() self.assertNotIn(h, certfp.values()) # print([apath, h]) # I do not know, why put this file? der = a.get_certificate_der(sig) apk.show_Certificate(c, True) apk.show_Certificate(c, False) self.assertEqual(hashlib.sha256(der).hexdigest(), h) pass else: for sig in a.get_signature_names(): c = a.get_certificate(sig) h = c.sha256_fingerprint.replace(" ","").lower() self.assertIn(h, certfp.values()) # Check that we get the same signature if we take the DER der = a.get_certificate_der(sig) self.assertEqual(hashlib.sha256(der).hexdigest(), h) if a.is_signed_v2(): if apath == "weird-compression-method.apk": with self.assertRaises(NotImplementedError): a.get_certificates_der_v2() elif apath == "v2-only-with-rsa-pkcs1-sha256-1024-cert-not-der.apk": # FIXME # Not sure what this one should do... but the certificate fingerprint is weird # as the hash over the DER is not the same when using the certificate continue else: for c in a.get_certificates_der_v2(): cert = x509.Certificate.load(c) h = cert.sha256_fingerprint.replace(" ","").lower() self.assertIn(h, certfp.values()) # Check that we get the same signature if we take the DER self.assertEqual(hashlib.sha256(c).hexdigest(), h) if a.is_signed_v3(): print(apath) if apath == "weird-compression-method.apk": with self.assertRaises(NotImplementedError): a.get_certificates_der_v3() elif apath == "v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk" or \ apath == "v3-only-cert-and-public-key-mismatch.apk": cert = x509.Certificate.load(a.get_certificates_der_v3()[0]) h = cert.sha256_fingerprint.replace(" ","").lower() self.assertNotIn(h, certfp.values()) else: for c in a.get_certificates_der_v3(): cert = x509.Certificate.load(c) h = cert.sha256_fingerprint.replace(" ","").lower() self.assertIn(h, certfp.values()) # Check that we get the same signature if we take the DER self.assertEqual(hashlib.sha256(c).hexdigest(), h)