From 16a6bd21946bfe3d75639ffd3d96957971b6553f Mon Sep 17 00:00:00 2001 From: erev0s Date: Fri, 20 Sep 2024 21:50:29 +0300 Subject: [PATCH] removed oscrypto --- androguard/cli/main.py | 12 +++--- androguard/util.py | 96 +++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 - requirements.txt | 3 +- tests/test_apk.py | 11 +++++ 5 files changed, 113 insertions(+), 10 deletions(-) diff --git a/androguard/cli/main.py b/androguard/cli/main.py index e8d86332..178d6420 100644 --- a/androguard/cli/main.py +++ b/androguard/cli/main.py @@ -11,7 +11,6 @@ from loguru import logger from pygments import highlight from pygments.lexers import get_lexer_by_name from pygments.formatters.terminal import TerminalFormatter -from oscrypto import asymmetric # internal modules from androguard.core.axml import ARSCParser @@ -22,6 +21,7 @@ from androguard.core.axml import AXMLPrinter from androguard.core.dex import get_bytecodes_method from androguard.util import readFile from androguard.ui import DynamicUI +from androguard.util import parse_public, calculate_fingerprint def androaxml_main( inp:str, @@ -375,12 +375,12 @@ def androsign_main(args_apk:list[str], args_hash:str, args_all:bool, show:bool) 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)) + parsed_key = parse_public(public_key) + print(f"Algorithm: {parsed_key.algorithm}") + print(f"Bit size: {parsed_key.bit_size}") + print(f"Fingerprint: {calculate_fingerprint(parsed_key).hex()}") try: - print("Hash Algorithm:", x509_public_key.asn1.hash_algo) + print(f"Hash Algorithm: {parsed_key.hash_algo}") except ValueError as ve: # RSA pkey does not have a hash algorithm pass diff --git a/androguard/util.py b/androguard/util.py index b020af5f..804b8866 100644 --- a/androguard/util.py +++ b/androguard/util.py @@ -1,7 +1,9 @@ - import sys from typing import Union, BinaryIO +from asn1crypto import keys, x509 +import hashlib +import binascii #  External dependencies # import asn1crypto @@ -93,3 +95,95 @@ def get_certificate_name_string(name:Union[dict,Name], short:bool=False, delimit 'organization_identifier': ("organizationIdentifier", "organizationIdentifier"), } return delimiter.join(["{}={}".format(_.get(attr, (attr, attr))[0 if short else 1], name[attr]) for attr in name]) + + +def parse_public(data): + from asn1crypto import pem, keys, x509 + """ + Loads a public key from a DER or PEM-formatted input. + Supports RSA, DSA, EC public keys, and X.509 certificates. + + :param data: A byte string of the public key or certificate + :raises ValueError: If the input data is not a known format + :return: A keys.PublicKeyInfo object containing the parsed public key + """ + + # Check if the data is in PEM format (starts with "-----") + if pem.detect(data): + type_name, _, der_bytes = pem.unarmor(data) + if type_name in ['PRIVATE KEY', 'RSA PRIVATE KEY']: + raise ValueError("The data specified appears to be a private key, not a public key.") + else: + # If not PEM, assume it's DER-encoded + der_bytes = data + + # Try to parse the data as PublicKeyInfo (standard public key structure) + try: + public_key_info = keys.PublicKeyInfo.load(der_bytes) + public_key_info.native # Fully parse the object (asn1crypto is lazy) + return public_key_info + except ValueError: + pass # Not a PublicKeyInfo structure + + # Try to parse the data as an X.509 certificate + try: + certificate = x509.Certificate.load(der_bytes) + public_key_info = certificate['tbs_certificate']['subject_public_key_info'] + public_key_info.native # Fully parse the object + return public_key_info + except ValueError: + pass # Not a certificate + + # Try to parse the data as RSAPublicKey + try: + rsa_public_key = keys.RSAPublicKey.load(der_bytes) + rsa_public_key.native # Fully parse the object + # Wrap the RSAPublicKey in PublicKeyInfo + return keys.PublicKeyInfo.wrap(rsa_public_key, 'rsa') + except ValueError: + pass # Not an RSAPublicKey structure + + raise ValueError("The data specified does not appear to be a known public key or certificate format.") + + +def calculate_fingerprint(key_object): + """ + Calculates a SHA-256 fingerprint of the public key based on its components. + + :param key_object: A keys.PublicKeyInfo object containing the parsed public key + :return: The fingerprint of the public key as a byte string + """ + + to_hash = None + + # RSA Public Key + if key_object.algorithm == 'rsa': + key = key_object['public_key'].parsed + # Prepare string with modulus and public exponent + to_hash = '%d:%d' % (key['modulus'].native, key['public_exponent'].native) + + # DSA Public Key + elif key_object.algorithm == 'dsa': + key = key_object['public_key'].parsed + params = key_object['algorithm']['parameters'] + # Prepare string with p, q, g, and public key + to_hash = '%d:%d:%d:%d' % ( + params['p'].native, + params['q'].native, + params['g'].native, + key.native, + ) + + # EC Public Key + elif key_object.algorithm == 'ec': + public_key = key_object['public_key'].native + # Prepare byte string with curve name and public key + to_hash = '%s:' % key_object.curve[1] + to_hash = to_hash.encode('utf-8') + public_key + + # Ensure to_hash is encoded as bytes if it's a string + if isinstance(to_hash, str): + to_hash = to_hash.encode('utf-8') + + # Return the SHA-256 hash of the formatted key data + return hashlib.sha256(to_hash).digest() \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 9ed17b65..115e1792 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,6 @@ apkInspector = ">=1.1.7" matplotlib = "*" networkx = "*" pyyaml = "*" -oscrypto = ">=1.3.0" [tool.setuptools.package_data] "androguard.core.api_specific_resources" = ["aosp_permissions/*.json", "api_permission_mappings/*.json"] diff --git a/requirements.txt b/requirements.txt index 9a5bf158..aeea9a63 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,5 +12,4 @@ loguru apkInspector>=1.2.1 matplotlib networkx -pyyaml -oscrypto>=1.3.0 \ No newline at end of file +pyyaml \ No newline at end of file diff --git a/tests/test_apk.py b/tests/test_apk.py index 0b115424..aa9fc53b 100644 --- a/tests/test_apk.py +++ b/tests/test_apk.py @@ -714,5 +714,16 @@ class APKTest(unittest.TestCase): self.assertEqual(a.get_app_name(locale='ru-rRU'), "values-ru-rRU") + def testPublicKeysofApk(self): + a = APK(os.path.join(test_dir, 'data/APK/com.example.android.wearable.wear.weardrawers.apk')) + pkeys = set(a.get_public_keys_der_v3() + a.get_public_keys_der_v2()) + for public_key in pkeys: + from androguard.util import parse_public + from androguard.util import calculate_fingerprint + parsed_key = parse_public(public_key) + self.assertEqual(parsed_key.algorithm, 'rsa') + self.assertEqual(parsed_key.bit_size, 2048) + self.assertEqual(calculate_fingerprint(parsed_key).hex(), '98917cd03c6277d73d58b661d614c442f2981a35a5aa122a61049215ba85c1d4') + if __name__ == '__main__': unittest.main(failfast=True)