UNPKG

node-apk

Version:

A library to parse Android application manifest and signature

96 lines (78 loc) 3.44 kB
/* * Copyright (c) 2021 XdevL. All rights reserved. * * This work is licensed under the terms of the MIT license. * For a copy, see <https://opensource.org/licenses/MIT>. */ import NodeForge from "node-forge"; type ForgeCertificate = NodeForge.pki.Certificate; class CertificateStore { private store = new Map<any, ForgeCertificate>(); constructor(...certificates: ForgeCertificate[]) { certificates.forEach((cert) => this.store.set(cert.subject.hash, cert)); } lookup(hash: any): ForgeCertificate|undefined { return this.store.get(hash); } leaves(): ForgeCertificate[] { const leaves = new Set(this.store.keys()); this.store.forEach((cert) => !cert.isIssuer(cert) && leaves.delete(cert.issuer.hash)); return Array.from(leaves).map((hash) => this.lookup(hash)!); } chain<T>(certificate: ForgeCertificate, create: (cert: ForgeCertificate, parent?: T) => T): T { const issuer = this.lookup(certificate.issuer.hash); if (issuer == null || certificate.isIssuer(certificate)) { return create(certificate) } else { return create(certificate, this.chain(issuer, create)); } } } export default class Certificate { public static fromPkcs7(buffer: Buffer): Certificate[] { const asn = NodeForge.asn1.fromDer(buffer.toString("binary")); const certificates = (NodeForge.pkcs7 as any).messageFromAsn1(asn) .certificates as ForgeCertificate[]; return Certificate.from(...certificates); } public static fromDer(...certificates: Buffer[]): Certificate[] { return Certificate.from(...certificates.map((der) => Certificate.certificateFromBytes(der))); } private static from(...certificates: ForgeCertificate[]): Certificate[] { const store = new CertificateStore(...certificates); return store.leaves().map((leaf) => store.chain(leaf, (cert, parent) => new Certificate(cert, parent))); } private static attributesToMap(attributes: any[]): Map<string, string> { return new Map((attributes as NodeForge.pki.CertificateField[]) .map((attr) => [attr.shortName, attr.value]) .filter((pair): pair is [string, string] => !!pair[0] && !!pair[1])); } private static certificateToBytes(certificate: ForgeCertificate): Buffer { return Buffer.from(NodeForge.asn1.toDer(NodeForge.pki.certificateToAsn1(certificate)) .getBytes(), "binary"); } private static certificateFromBytes(bytes: Buffer): ForgeCertificate { return NodeForge.pki.certificateFromAsn1( NodeForge.asn1.fromDer(bytes.toString("binary"))); } public readonly parent?: Certificate; public readonly serial: string; public readonly validFrom: Date; public readonly validUntil: Date; public readonly issuer: Map<string, string>; public readonly subject: Map<string, string>; public readonly bytes: Buffer; private constructor(input: ForgeCertificate, parent?: Certificate) { const certificate = input; this.serial = certificate.serialNumber; this.validFrom = certificate.validity.notBefore; this.validUntil = certificate.validity.notAfter; this.issuer = Certificate.attributesToMap(certificate.issuer.attributes); this.subject = Certificate.attributesToMap(certificate.subject.attributes); this.bytes = Certificate.certificateToBytes(certificate); this.parent = parent; } get chain(): Certificate[] { return this.parent ? this.parent.chain.concat(this) : [this]; } }