Certificate
Represents a signing certificate from an APK file with full chain support.
Overview
The Certificate class provides access to the signing certificates extracted from the APK's META-INF directory. It supports full certificate chain traversal.
import { Apk } from "node-apk";
const apk = new Apk("app.apk");
const certs = await apk.getCertificateInfo();Properties
serial
readonly serial: stringThe serial number of the certificate.
Example
console.log(`Serial: ${cert.serial}`);
// Output: Serial: 01:23:45:67:89:ab:cd:efvalidFrom
readonly validFrom: DateThe start date of the certificate's validity period.
Example
console.log(`Valid from: ${cert.validFrom.toISOString()}`);
// Output: Valid from: 2021-01-01T00:00:00.000ZvalidUntil
readonly validUntil: DateThe end date of the certificate's validity period.
Example
const now = new Date();
if (cert.validUntil < now) {
console.warn("Certificate has expired!");
}issuer
readonly issuer: Map<string, string>A map of the certificate issuer's attributes.
Common Keys
| Key | Description | Example |
|---|---|---|
CN | Common Name | "My CA" |
O | Organization | "My Company Inc." |
OU | Organizational Unit | "Engineering" |
C | Country | "US" |
ST | State | "California" |
L | Locality | "San Francisco" |
Example
console.log(`Issuer CN: ${cert.issuer.get("CN")}`);
console.log(`Issuer O: ${cert.issuer.get("O")}`);
// Print all issuer attributes
for (const [key, value] of cert.issuer) {
console.log(` ${key}: ${value}`);
}subject
readonly subject: Map<string, string>A map of the certificate subject's attributes.
Example
console.log(`Subject CN: ${cert.subject.get("CN")}`);
console.log(`Subject O: ${cert.subject.get("O")}`);bytes
readonly bytes: BufferThe raw certificate data in DER encoding.
Example
// Get base64-encoded certificate
const base64 = cert.bytes.toString("base64");
console.log(`PEM-like:\n-----BEGIN CERTIFICATE-----\n${base64}\n-----END CERTIFICATE-----`);
// Calculate fingerprint
import { createHash } from "crypto";
const sha256 = createHash("sha256").update(cert.bytes).digest("hex");
console.log(`SHA-256: ${sha256}`);parent
readonly parent: Certificate | undefinedThe parent (issuer) certificate in the chain. Undefined if self-signed or root.
Example
if (cert.parent) {
console.log(`Signed by: ${cert.parent.subject.get("CN")}`);
} else {
console.log("Self-signed certificate");
}chain
readonly chain: Certificate[]The complete certificate chain from root to this certificate.
Example
console.log("Certificate Chain:");
for (let i = 0; i < cert.chain.length; i++) {
const c = cert.chain[i];
console.log(` ${i + 1}. ${c.subject.get("CN")}`);
}
// Output:
// Certificate Chain:
// 1. Root CA
// 2. Intermediate CA
// 3. My ApplicationStatic Methods
fromPkcs7()
static fromPkcs7(buffer: Buffer): Certificate[]Creates Certificate instances from a PKCS#7 signature block.
Parameters
| Parameter | Type | Description |
|---|---|---|
buffer | Buffer | PKCS#7 signature block data |
Returns
Certificate[] - Array of certificates found in the signature block.
fromDer()
static fromDer(...certificates: Buffer[]): Certificate[]Creates Certificate instances from DER-encoded certificate data.
Parameters
| Parameter | Type | Description |
|---|---|---|
certificates | ...Buffer | One or more DER-encoded certs |
Returns
Certificate[] - Array of certificates with resolved chains.
Examples
Check Certificate Validity
const certs = await apk.getCertificateInfo();
for (const cert of certs) {
const now = new Date();
if (now < cert.validFrom) {
console.warn(`Certificate not yet valid: ${cert.subject.get("CN")}`);
} else if (now > cert.validUntil) {
console.error(`Certificate expired: ${cert.subject.get("CN")}`);
} else {
const daysUntilExpiry = Math.floor(
(cert.validUntil.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)
);
console.log(`Valid for ${daysUntilExpiry} more days`);
}
}Verify Certificate Chain
function verifyChain(cert: Certificate): boolean {
// Walk up the chain
let current: Certificate | undefined = cert;
while (current) {
// Check if self-signed (issuer == subject)
const isSelfSigned = current.issuer.get("CN") === current.subject.get("CN");
if (!current.parent && !isSelfSigned) {
console.error("Chain incomplete - missing root certificate");
return false;
}
current = current.parent;
}
return true;
}
const certs = await apk.getCertificateInfo();
for (const cert of certs) {
if (!verifyChain(cert)) {
console.error(`Invalid chain for ${cert.subject.get("CN")}`);
}
}Compare Certificates
function certificatesMatch(cert1: Certificate, cert2: Certificate): boolean {
return cert1.bytes.equals(cert2.bytes);
}
// Check if APK was signed with expected certificate
const expectedCert = Certificate.fromDer(expectedDerBuffer)[0];
const actualCerts = await apk.getCertificateInfo();
const isSignedByExpected = actualCerts.some(c =>
certificatesMatch(c, expectedCert)
);
if (!isSignedByExpected) {
console.error("APK not signed by expected certificate!");
}Extract Certificate Fingerprints
import { createHash } from "crypto";
function getFingerprints(cert: Certificate) {
return {
sha256: createHash("sha256").update(cert.bytes).digest("hex"),
sha1: createHash("sha1").update(cert.bytes).digest("hex"),
md5: createHash("md5").update(cert.bytes).digest("hex"),
};
}
const certs = await apk.getCertificateInfo();
for (const cert of certs) {
const prints = getFingerprints(cert);
console.log(`Certificate: ${cert.subject.get("CN")}`);
console.log(` SHA-256: ${prints.sha256}`);
console.log(` SHA-1: ${prints.sha1}`);
}