Verified against a real signed installer and the unsigned fixture in fixtures/.
This corner of the crate is thinly documented and easy to get subtly wrong. See the
SignerInfo.sid note in the README.
use object::read::pe::PeFile64;
use authenticode::AttributeCertificateIterator;
let pe = PeFile64::parse(&*data)?; // Err if not a PE
match AttributeCertificateIterator::new(&pe)? { // Result<Option<Iterator>>
None => /* UNSIGNED — no attribute certificate table */,
Some(iter) => for cert in iter { // item: Result<AttributeCertificate>
let sig = cert?.get_authenticode_signature()?; // owned AuthenticodeSignature
// ...
}
}sig.signer_info() -> &cms::signed_data::SignerInfo.digest_alg.oid→ digest algorithm OID (Display = dotted string).unsigned_attrs: Option<Attributes>→.iter()yields attrs with.oid(timestamp lives here).sid: SignerIdentifier→ identifies the signer (leaf) cert
sig.certificates() -> impl Iterator<Item = &x509_cert::Certificate>— the full chain bundle (leaf + CAs)sig.digest() -> &[u8],sig.signature() -> &[u8],sig.encapsulated_content() -> Option<&[u8]>
certificates() is NOT ordered leaf-first — on the real installer .next() was the Sectigo root, not the signer. The signer (leaf) MUST be selected by matching SignerInfo.sid:
use cms::signed_data::SignerIdentifier;
let leaf: Option<&x509_cert::Certificate> = match &si.sid {
SignerIdentifier::IssuerAndSerialNumber(isn) => sig.certificates().find(|c| {
c.tbs_certificate.issuer == isn.issuer
&& c.tbs_certificate.serial_number == isn.serial_number
}),
SignerIdentifier::SubjectKeyIdentifier(_) => None, // rare for Authenticode; handle later
};c.tbs_certificate.subject/.issuer—Name,Display= RFC 4514 (e.g.CN=...,O=Acme,C=US)c.tbs_certificate.serial_number—SerialNumber(PartialEq works for sid match)c.tbs_certificate.validity.not_after—Display= RFC 3339 (2024-11-18T23:59:59Z); for unix secs use.to_unix_duration().as_secs()on the innerTime
1.2.840.113549.1.9.6— PKCS#9 counterSignature (legacy Authenticode timestamp; seen on ce-cli)1.3.6.1.4.1.311.3.3.1— Microsoft RFC-3161 timestamp token (modern) Presence of either ⇒ timestamped.
2.16.840.1.101.3.4.2.1SHA-256 (good)1.3.14.3.2.26SHA-1 (deprecated; ce-cli's primary sig uses this)
- No need to separately
cms::SignedData::from_der—AuthenticodeSignaturealready exposessigner_info()+certificates(). Thesignature.rsmodule takes&AuthenticodeSignature, does the sid-based leaf selection, and returns ownedSignatureInfo { digest_algo_oid, has_timestamp, leaf_cert }. pe.rsreturns an ownedOption<AuthenticodeSignature>(the type owns its parsed data; from_bytes/get_authenticode_signature returnSelf, not a borrow).cert.rstakes&x509_cert::Certificate(the leaf), unchanged in spirit.