Skip to content

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.

typescript
import { Apk } from "node-apk";

const apk = new Apk("app.apk");
const certs = await apk.getCertificateInfo();

Properties

serial

typescript
readonly serial: string

The serial number of the certificate.

Example

typescript
console.log(`Serial: ${cert.serial}`);
// Output: Serial: 01:23:45:67:89:ab:cd:ef

validFrom

typescript
readonly validFrom: Date

The start date of the certificate's validity period.

Example

typescript
console.log(`Valid from: ${cert.validFrom.toISOString()}`);
// Output: Valid from: 2021-01-01T00:00:00.000Z

validUntil

typescript
readonly validUntil: Date

The end date of the certificate's validity period.

Example

typescript
const now = new Date();
if (cert.validUntil < now) {
  console.warn("Certificate has expired!");
}

issuer

typescript
readonly issuer: Map<string, string>

A map of the certificate issuer's attributes.

Common Keys

KeyDescriptionExample
CNCommon Name"My CA"
OOrganization"My Company Inc."
OUOrganizational Unit"Engineering"
CCountry"US"
STState"California"
LLocality"San Francisco"

Example

typescript
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

typescript
readonly subject: Map<string, string>

A map of the certificate subject's attributes.

Example

typescript
console.log(`Subject CN: ${cert.subject.get("CN")}`);
console.log(`Subject O: ${cert.subject.get("O")}`);

bytes

typescript
readonly bytes: Buffer

The raw certificate data in DER encoding.

Example

typescript
// 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

typescript
readonly parent: Certificate | undefined

The parent (issuer) certificate in the chain. Undefined if self-signed or root.

Example

typescript
if (cert.parent) {
  console.log(`Signed by: ${cert.parent.subject.get("CN")}`);
} else {
  console.log("Self-signed certificate");
}

chain

typescript
readonly chain: Certificate[]

The complete certificate chain from root to this certificate.

Example

typescript
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 Application

Static Methods

fromPkcs7()

typescript
static fromPkcs7(buffer: Buffer): Certificate[]

Creates Certificate instances from a PKCS#7 signature block.

Parameters

ParameterTypeDescription
bufferBufferPKCS#7 signature block data

Returns

Certificate[] - Array of certificates found in the signature block.


fromDer()

typescript
static fromDer(...certificates: Buffer[]): Certificate[]

Creates Certificate instances from DER-encoded certificate data.

Parameters

ParameterTypeDescription
certificates...BufferOne or more DER-encoded certs

Returns

Certificate[] - Array of certificates with resolved chains.

Examples

Check Certificate Validity

typescript
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

typescript
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

typescript
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

typescript
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}`);
}
  • Apk - Certificate extraction
  • Examples - Certificate examples

Released under the MIT License.