Ian J. Oppermann

62 papers A 1B 17C 2Journal 32Unranked 10
YearRankTypeTitle / Venue / Authors
2025 A conf
AsiaCCS
M. A. P. Chamikara, Seung Ick Jang, Ian J. Oppermann, Dongxi Liu, Musotto Roberto, Sushmita Ruj, Arindam Pal, Meisam Mohammady, Seyit Camtepe, Sylvia Young, Chris Dorrian, Nasir David
2024 conf
ProvSec (2)
Xu Wang, Baihe Ma, Ren Ping Liu, Ian J. Oppermann
2024 J jnl
Digit. Gov. Res. Pract.
Grace Billiris, Asif Gill, Ian J. Oppermann, Mahmood Khan Niazi
2023 J jnl
CoRR
M. A. P. Chamikara, Seung Ick Jang, Ian J. Oppermann, Dongxi Liu, Musotto Roberto, Sushmita Ruj, Arindam Pal, Meisam Mohammady, Seyit Camtepe, Sylvia Young, Chris Dorrian, Nasir David
2023 J jnl
J. Vis.
Dong Yu, Ian J. Oppermann, Jie Liang, Xiaoru Yuan, Nguyen Quang Vinh
2020 J jnl
CoRR
Mika Ylianttila, Raimo Kantola, Andrei V. Gurtov, Lorenzo Mucchi, Ian J. Oppermann, Zheng Yan, Tri Hong Nguyen, Fei Liu, Tharaka Mawanane Hewa, Madhusanka Liyanage, Ijaz Ahmad, Juha Partala, Robert Abbas, Artur Hecker, Sara Jayousi, Alessio Martinelli, Stefano Caputo, Jonathan Bechtold, Ivan Morales, Razvan-Andrei Stoica, Giuseppe Abreu, Shahriar Shahabuddin, Erdal Panayirci, Harald Haas, Tanesh Kumar, Basak Ozan Özparlak, Juha Röning
2020 conf
6G SUMMIT
Ian J. Oppermann, Jakub Nabaglo, Wilko Henecka
2020 B conf
ICPR
Wanming Huang, Richard Yida Xu, Shuai Jiang, Xuan Liang, Ian J. Oppermann
2020 J jnl
CoRR
Marja Matinmikko-Blue, Sirpa Aalto, Muhammad Imran Asghar, Hendrik Berndt, Yan Chen, Sudhir Dixit, Risto Jurva, Pasi Karppinen, Markku Kekkonen, Marianne Kinnula, Panagiotis Kostakos, Johanna Lindberg, Edward Mutafungwa, Kirsi Ojutkangas, Elina Rossi, Seppo Yrjölä, Anssi Öörni, Petri Ahokangas, Muhammad Zeeshan Asghar, Fan Chen, Netta Iivari, Marcos Katz, Atte Kinnula, Josef Noll, Harri Oinas-Kukkonen, Ian J. Oppermann, Ella Peltonen, Hanna Saarela, Harri Saarnisaari, Anna Suorsa, Gustav Wikström, Volker Ziegler
2019 C conf
ACML
Wanming Huang, Richard Yi Da Xu, Ian J. Oppermann
2019 C conf
ACML
Wanming Huang, Richard Yi Da Xu, Ian J. Oppermann
2019 J jnl
CoRR
Wanming Huang, Yida Xu, Ian J. Oppermann
2014 J jnl
Phys. Commun.
Kegen Yu, Ian J. Oppermann, Eryk Dutkiewicz, Ian Sharp, Günther Retscher
2014 B conf
WCNC
Beeshanga Abewardana Jayawickrama, Eryk Dutkiewicz, Ian J. Oppermann, Markus Mueck
2013 B conf
GLOBECOM
Beeshanga Abewardana Jayawickrama, Eryk Dutkiewicz, Gengfa Fang, Ian J. Oppermann, Markus Mueck
2013 conf
ICC
Beeshanga Abewardana Jayawickrama, Eryk Dutkiewicz, Ian J. Oppermann, Gengfa Fang, Jie Ding
2013 J jnl
Int. J. Wirel. Inf. Networks
Abbas Jamalipour, Ian J. Oppermann, Nirwan Ansari, Graeme Woodward
2010 J jnl
IEEE Trans. Wirel. Commun.
Alberto Rabbachin, Tony Q. S. Quek, Pedro C. Pinto, Ian J. Oppermann, Moe Z. Win
2009 J jnl
IEEE Trans. Veh. Technol.
Jussi Haapola, Alberto Rabbachin, Leonardo Goratti, Carlos A. Pomalaza-Raez, Ian J. Oppermann
2008 conf
VTC Spring
Kegen Yu, Y. Jay Guo, Ian J. Oppermann
2007 J jnl
Wirel. Pers. Commun.
Leonardo Goratti, Jussi Haapola, Ian J. Oppermann
2007 B conf
PIMRC
Jean-Philippe Montillet, Kegen Yu, Ian J. Oppermann
2006 J jnl
J. VLSI Signal Process.
Sakari Tiuraniemi, Lucian-Vasile Stoica, Alberto Rabbachin, Ian J. Oppermann
2006 B conf
PIMRC
Alberto Rabbachin, Ian J. Oppermann, Benoît Denis
2006 B conf
PIMRC
Lucian-Vasile Stoica, Ian J. Oppermann
2006 J jnl
IEEE Trans. Wirel. Commun.
Kegen Yu, Ian J. Oppermann
2006 J jnl
Signal Process.
Kegen Yu, Jean-Philippe Montillet, Alberto Rabbachin, Paul Cheong, Ian J. Oppermann
2006 J jnl
EURASIP J. Adv. Signal Process.
Thomas Kaiser, Ian J. Oppermann, Domenico Porcino
2005 conf
ISCAS (1)
Lucian-Vasile Stoica, Sakari Tiuraniemi, Heikki Repo, Ian J. Oppermann
2005 J jnl
IEEE Trans. Veh. Technol.
Lucian-Vasile Stoica, Alberto Rabbachin, Heikki Olavi Repo, Teemu Sakari Tiuraniemi, Ian J. Oppermann
2005 J jnl
IEEE Trans. Commun.
Mohammad N. Patwary, Predrag B. Rapajic, Ian J. Oppermann
2005 B conf
ISIT
Kegen Yu, Ian J. Oppermann
2005 J jnl
IEEE Trans. Wirel. Commun.
Kegen Yu, Ian J. Oppermann
2005 conf
ISSPA
Kegen Yu, Ian J. Oppermann
2004 B conf
PIMRC
Lucian-Vasile Stoica, Sakari Tiuraniemi, Heikki Repo, Alberto Rabbachin, Ian J. Oppermann
2004 J jnl
Wirel. Pers. Commun.
Ian J. Oppermann
2004 J jnl
IEEE Commun. Mag.
Ian J. Oppermann, Lucian-Vasile Stoica, Alberto Rabbachin, Zach Shelby, Jussi Haapola
2003 B conf
GLOBECOM
Mohammad N. Patwary, Predrag B. Rapajic, Xun Shao, Ian J. Oppermann
2003 J jnl
Int. J. Wirel. Inf. Networks
Ian J. Oppermann
2003 J jnl
IEEE Trans. Wirel. Commun.
Lars K. Rasmussen, Ian J. Oppermann
2003 B conf
PIMRC
Matti Hämäläinen, Jani Saloranta, Juha-Pekka Mäkelä, Ian J. Oppermann, Tero Patana
2003 B conf
PIMRC
Matti Hämäläinen, Jani Saloranta, Juha-Pekka Mäkelä, Ian J. Oppermann, Tero Patana
2003 J jnl
Int. J. Wirel. Inf. Networks
Matti Hämäläinen, Jani Saloranta, Juha-Pekka Mäkelä, Ian J. Oppermann, Tero Patana
2002 J jnl
IEEE Commun. Lett.
Ian J. Oppermann
2002 conf
PWC
Mohammad N. Patwary, Predrag B. Rapajic, Ian J. Oppermann
2002 conf
PWC
Mohammad N. Patwary, Predrag B. Rapajic, Ian J. Oppermann
2002 B conf
PIMRC
Ian J. Oppermann
2001 B conf
GLOBECOM
Lars K. Rasmussen, Ian J. Oppermann
2001 B conf
GLOBECOM
Peter B. Darwood, Paul D. Alexander, K. Wacker, Ian J. Oppermann
2001 conf
ICC
Peter B. Darwood, Paul D. Alexander, Ian J. Oppermann
2000 J jnl
IEEE Trans. Commun.
Ian J. Oppermann, Predrag B. Rapajic
2000 J jnl
IEEE J. Sel. Areas Commun.
Ian J. Oppermann, Pieter van Rooyen, Ryuji Kohno
2000 B conf
PIMRC
Ian J. Oppermann, Matti Latva-aho, Timo Kumpumäki
1999 J jnl
IEEE J. Sel. Areas Commun.
Ian J. Oppermann, Pieter van Rooyen, Ryuji Kohno
1998 J jnl
Wirel. Networks
Pieter van Rooyen, Ryuji Kohno, Ian J. Oppermann
1998 J jnl
Wirel. Networks
Ian J. Oppermann, Pieter van Rooyen, Branka Vucetic
1998 J jnl
Wirel. Pers. Commun.
Ian J. Oppermann, Clinton Grant
1997 J jnl
IEEE Trans. Commun.
Ian J. Oppermann, Branka Vucetic
1997 J jnl
IEEE Trans. Commun.
Ian J. Oppermann
1997 conf
ICC (1)
Ian J. Oppermann, Jaakko J. Talvitie, David K. Hunter
1996 B conf
PIMRC
Jaakko J. Talvitie, Veikko Hovinen, Matti Hämäläinen, Ian J. Oppermann
1995 B conf
PIMRC
Ian J. Oppermann, Benjamin White, Branka Vucetic
redb/extractors/apk_extractors/apk_signature.py
← Index redb/extractors/apk_extractors/apk_signature.py python
import hashlib
import inspect
from datetime import datetime, timezone
from typing import Any, Optional, Tuple

from redb.extractors.enum import Tag
from redb.extractors.apk_extractor import APKExtractor
from redb.models.dataclasses import APKCertificate, APKCodeSigningInfo


# OID to RFC 4514 short name mapping
_OID_SHORT_NAMES = {
    '2.5.4.3': 'CN', '2.5.4.6': 'C', '2.5.4.7': 'L', '2.5.4.8': 'ST',
    '2.5.4.10': 'O', '2.5.4.11': 'OU', '1.2.840.113549.1.9.1': 'E',
    '2.5.4.5': 'SERIALNUMBER', '2.5.4.12': 'T', '2.5.4.42': 'GN',
    '2.5.4.4': 'SN', '2.5.4.9': 'STREET', '2.5.4.17': 'POSTALCODE',
}


def _asn1_name_to_rfc4514(name):
    """Convert an asn1crypto x509.Name to RFC 4514 string (CN=..., O=...)."""
    parts = []
    for rdn in name.chosen:
        for attr in rdn:
            oid = attr['type'].dotted
            val = attr['value'].native
            short = _OID_SHORT_NAMES.get(oid, oid)
            parts.append(f'{short}={val}')
    return ', '.join(parts)


class APKSignatureExtractor(APKExtractor):

    def __init__(
        self, filepath, log, exporters=None, index_prefix=None,
        known_benign=False, known_malicious=False,
        apk=None,
    ):
        super().__init__(
            filepath, log, exporters, index_prefix,
            known_benign, known_malicious, apk,
        )
        self.signing_info = None
        self.log.debug(inspect.currentframe().f_code.co_name)

    def tag(self):
        return Tag.APK_SIGNATURE.value

    def _detect_signature_schemes(self):
        """Detect which APK signature schemes are present."""
        schemes = []
        try:
            if self.apk.is_signed_v1():
                schemes.append(1)
        except Exception:
            pass
        try:
            if self.apk.is_signed_v2():
                schemes.append(2)
        except Exception:
            pass
        try:
            if self.apk.is_signed_v3():
                schemes.append(3)
        except Exception:
            pass
        return schemes

    def _extract_certificate_info(self, cert, source_scheme=None):
        """Extract certificate details from an androguard certificate object."""
        try:
            # Subject and Issuer — asn1crypto doesn't have rfc4514_string, convert manually
            subject = _asn1_name_to_rfc4514(cert.subject)
            issuer = _asn1_name_to_rfc4514(cert.issuer)

            # Serial number
            serial = str(cert.serial_number)

            # Validity
            valid_from = cert.not_valid_before
            valid_to = cert.not_valid_after
            if isinstance(valid_from, datetime):
                valid_from = valid_from.strftime("%Y-%m-%d %H:%M:%S")
            else:
                valid_from = str(valid_from)
            if isinstance(valid_to, datetime):
                valid_to = valid_to.strftime("%Y-%m-%d %H:%M:%S")
            else:
                valid_to = str(valid_to)

            # Thumbprints
            cert_der = cert.dump()
            thumbprint_sha1 = hashlib.sha1(cert_der).hexdigest()
            thumbprint_sha256 = hashlib.sha256(cert_der).hexdigest()

            # Algorithm
            algorithm = None
            try:
                algorithm = cert.hash_algo
            except Exception:
                try:
                    algorithm = cert.signature_algo
                except Exception:
                    pass

            # Key size
            key_size = None
            try:
                pub_key = cert.public_key
                if hasattr(pub_key, 'bit_size'):
                    key_size = pub_key.bit_size
                elif hasattr(pub_key, 'key_size'):
                    key_size = pub_key.key_size
            except Exception:
                pass

            # Self-signed check
            is_self_signed = cert.subject == cert.issuer

            return APKCertificate(
                subject=subject,
                issuer=issuer,
                serial_number=serial,
                valid_from=valid_from,
                valid_to=valid_to,
                thumbprint_sha1=thumbprint_sha1,
                thumbprint_sha256=thumbprint_sha256,
                algorithm=algorithm,
                key_size=key_size,
                is_self_signed=is_self_signed,
                certificate_source_scheme=source_scheme,
            )
        except Exception as e:
            self.log.error(f"Error extracting certificate info: {e}")
            return None

    def _get_signer_certificate(self, schemes) -> Tuple[Optional[APKCertificate], Optional[str]]:
        """Identify the actual signer certificate using V3>V2>V1 priority.

        V2/V3: certificates[0] is the signer per spec.
        V1: androguard's get_certificate() does SignerInfo matching internally.

        Returns (signer_cert_info, source_scheme) or (None, None).
        """
        # V3 takes priority (Android 9+)
        if 3 in schemes:
            try:
                certs = self.apk.get_certificates_v3()
                if certs:
                    cert_info = self._extract_certificate_info(certs[0], source_scheme='v3')
                    if cert_info:
                        return cert_info, 'v3'
            except Exception as e:
                self.log.debug(f"V3 signer extraction failed: {e}")

        # V2 next (Android 7.0+)
        if 2 in schemes:
            try:
                certs = self.apk.get_certificates_v2()
                if certs:
                    cert_info = self._extract_certificate_info(certs[0], source_scheme='v2')
                    if cert_info:
                        return cert_info, 'v2'
            except Exception as e:
                self.log.debug(f"V2 signer extraction failed: {e}")

        # V1 fallback — androguard does SignerInfo.issuerAndSerialNumber matching
        if 1 in schemes:
            try:
                names = self.apk.get_signature_names()
                if names:
                    cert = self.apk.get_certificate(names[0])
                    if cert:
                        cert_info = self._extract_certificate_info(cert, source_scheme='v1')
                        if cert_info:
                            return cert_info, 'v1'
            except Exception as e:
                self.log.debug(f"V1 signer extraction failed: {e}")

        return None, None

    def _extract_all_certificates(self, schemes):
        """Extract all certificates with their source scheme tracked.

        Uses scheme-specific methods to preserve source information,
        deduplicating by thumbprint_sha1 and merging source schemes.
        """
        # cert thumbprint -> (APKCertificate, set of source schemes)
        seen = {}

        for scheme_num, method_name in [
            (3, 'get_certificates_v3'),
            (2, 'get_certificates_v2'),
            (1, 'get_certificates_v1'),
        ]:
            if scheme_num not in schemes:
                continue
            try:
                method = getattr(self.apk, method_name, None)
                if method is None:
                    continue
                certs = method()
                if not certs:
                    continue
                for cert in certs:
                    cert_info = self._extract_certificate_info(cert, source_scheme=f'v{scheme_num}')
                    if cert_info:
                        key = cert_info.thumbprint_sha1
                        if key in seen:
                            # Merge source schemes
                            existing_cert, existing_schemes = seen[key]
                            existing_schemes.add(f'v{scheme_num}')
                        else:
                            seen[key] = (cert_info, {f'v{scheme_num}'})
            except Exception as e:
                self.log.warning(f"Error extracting v{scheme_num} certificates: {e}")

        # Build final list with merged source schemes
        result = []
        for cert_info, source_schemes in seen.values():
            cert_info.certificate_source_scheme = ','.join(sorted(source_schemes))
            result.append(cert_info)

        return result

    def extract(self):
        if not self._is_valid_apk():
            self.log.error(f"Invalid APK for {self.hash.sha256}")
            return None

        schemes = self._detect_signature_schemes()
        is_signed = len(schemes) > 0

        # Extract all certificates with source scheme tracking
        certificates = self._extract_all_certificates(schemes)

        # Identify the actual signer using V3>V2>V1 priority
        signer_cert, signer_scheme = self._get_signer_certificate(schemes)

        apk_signer_subject = signer_cert.subject if signer_cert else None
        apk_signer_issuer = signer_cert.issuer if signer_cert else None

        self.signing_info = APKCodeSigningInfo(
            _id=self.sha256,
            is_signed=is_signed,
            signature_scheme_versions=schemes,
            number_of_certificates=len(certificates),
            x509_certificates=certificates if certificates else None,
            apk_signer_subject=apk_signer_subject,
            apk_signer_issuer=apk_signer_issuer,
        )
        return self.signing_info

    def prepare_export_data(self, exporter_type: str) -> Any:
        if exporter_type == "ClickHouseExporter":
            if not self.signing_info:
                return None

            current_time = datetime.now(timezone.utc)
            sig = self.signing_info

            # Signature summary table
            signature_data = [[
                self.sha256,
                int(sig.is_signed),
                sig.signature_scheme_versions,
                sig.number_of_certificates,
                sig.apk_signer_subject,
                sig.apk_signer_issuer,
                current_time,
            ]]

            # Certificates table (one row per certificate)
            certificates_data = []
            if sig.x509_certificates:
                for cert in sig.x509_certificates:
                    certificates_data.append([
                        self.sha256,
                        cert.subject,
                        cert.issuer,
                        cert.serial_number,
                        datetime.strptime(cert.valid_from, "%Y-%m-%d %H:%M:%S") if cert.valid_from else None,
                        datetime.strptime(cert.valid_to, "%Y-%m-%d %H:%M:%S") if cert.valid_to else None,
                        cert.thumbprint_sha1,
                        cert.algorithm,
                        cert.key_size,
                        int(cert.is_self_signed),
                        cert.certificate_source_scheme,
                        current_time,
                    ])

            return {
                'multi_table': True,
                'signature': {
                    'table': 'redb_apk_signature',
                    'data': signature_data,
                    'column_names': [
                        'sha256',
                        'is_signed', 'signature_scheme_versions',
                        'number_of_certificates',
                        'apk_signer_subject', 'apk_signer_issuer',
                        'analysis_date',
                    ],
                    'column_type_names': [
                        'FixedString(64)',
                        'UInt8', 'Array(UInt8)',
                        'UInt8',
                        'Nullable(String)', 'Nullable(String)',
                        "DateTime64(3, 'UTC')",
                    ],
                },
                'certificates': {
                    'table': 'redb_apk_certificates',
                    'data': certificates_data,
                    'column_names': [
                        'sha256', 'certificate_subject', 'certificate_issuer',
                        'certificate_serial_number',
                        'certificate_valid_from', 'certificate_valid_to',
                        'certificate_thumbprint',
                        'certificate_algorithm', 'key_size', 'is_self_signed',
                        'certificate_source_scheme',
                        'analysis_date',
                    ],
                    'column_type_names': [
                        'FixedString(64)', 'String', 'String', 'String',
                        "DateTime64(3, 'UTC')", "DateTime64(3, 'UTC')",
                        'FixedString(40)',
                        'LowCardinality(String)', 'Nullable(UInt16)', 'UInt8',
                        'LowCardinality(String)',
                        "DateTime64(3, 'UTC')",
                    ],
                },
            }

    def get_clickhouse_table(self) -> str:
        return "redb_apk_signature"