mirror of
https://github.com/androguard/androguard.git
synced 2024-11-23 05:00:11 +00:00
commit
a458772eac
@ -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
|
||||
|
@ -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()
|
@ -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"]
|
||||
|
@ -13,4 +13,3 @@ apkInspector>=1.2.1
|
||||
matplotlib
|
||||
networkx
|
||||
pyyaml
|
||||
oscrypto>=1.3.0
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user