rust-apk 1.0.0

Build, align, sign, verify, and inspect Android APKs in pure Rust
Documentation
//! X.509 certificates as embedded in APK signatures.

use std::fmt;

use der::{Decode, Encode};
use sha2::{Digest, Sha256};

use super::{KeyError, PublicKey};

/// An X.509 certificate, kept with its exact DER encoding.
///
/// APK signatures embed certificates verbatim, and an app's identity on
/// Android is the digest of those bytes, so the original encoding is
/// preserved rather than re-encoded.
#[derive(Clone)]
pub struct Certificate {
    der: Vec<u8>,
    parsed: x509_cert::Certificate,
}

impl Certificate {
    pub fn from_der(der: &[u8]) -> Result<Self, KeyError> {
        let parsed = x509_cert::Certificate::from_der(der).map_err(KeyError::Certificate)?;
        Ok(Self { der: der.to_vec(), parsed })
    }

    /// The certificate's DER encoding, as embedded in signatures.
    pub fn der(&self) -> &[u8] {
        &self.der
    }

    /// The parsed certificate.
    pub fn x509(&self) -> &x509_cert::Certificate {
        &self.parsed
    }

    /// The SHA-256 digest of the DER encoding: the "signing certificate
    /// digest" Google Play and `apksigner` report.
    pub fn sha256_fingerprint(&self) -> [u8; 32] {
        Sha256::digest(&self.der).into()
    }

    /// The subject distinguished name, e.g. `CN=Android Debug,O=Android,C=US`.
    pub fn subject(&self) -> String {
        self.parsed.tbs_certificate().subject().to_string()
    }

    pub fn issuer(&self) -> String {
        self.parsed.tbs_certificate().issuer().to_string()
    }

    /// The serial number's big-endian bytes.
    pub fn serial_number(&self) -> &[u8] {
        self.parsed.tbs_certificate().serial_number().as_bytes()
    }

    /// The DER `SubjectPublicKeyInfo`.
    pub fn public_key_der(&self) -> Result<Vec<u8>, KeyError> {
        self.parsed.tbs_certificate().subject_public_key_info().to_der().map_err(KeyError::Certificate)
    }

    pub fn public_key(&self) -> Result<PublicKey, KeyError> {
        PublicKey::from_spki_der(&self.public_key_der()?)
    }
}

impl PartialEq for Certificate {
    fn eq(&self, other: &Self) -> bool {
        self.der == other.der
    }
}

impl Eq for Certificate {}

impl fmt::Debug for Certificate {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Certificate")
            .field("subject", &self.subject())
            .field("sha256", &hex(&self.sha256_fingerprint()))
            .finish()
    }
}

pub(crate) fn hex(bytes: &[u8]) -> String {
    bytes.iter().map(|b| format!("{b:02x}")).collect()
}