diff options
-rw-r--r-- | packages/server/src/helpers/getCertificateInfo.ts | 116 |
1 files changed, 47 insertions, 69 deletions
diff --git a/packages/server/src/helpers/getCertificateInfo.ts b/packages/server/src/helpers/getCertificateInfo.ts index c70ec6b..021ad77 100644 --- a/packages/server/src/helpers/getCertificateInfo.ts +++ b/packages/server/src/helpers/getCertificateInfo.ts @@ -1,102 +1,80 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -// `ASN1HEX` exists in the lib but not in its typings -// @ts-ignore 2305 -import { X509, zulutodate, ASN1HEX } from 'jsrsasign'; +import { AsnParser } from '@peculiar/asn1-schema'; +import { Certificate, BasicConstraints, id_ce_basicConstraints } from '@peculiar/asn1-x509'; export type CertificateInfo = { - issuer: { [key: string]: string }; - subject: { [key: string]: string }; + issuer: Issuer; + subject: Subject; version: number; basicConstraintsCA: boolean; notBefore: Date; notAfter: Date; }; -type ExtInfo = { - critical: boolean; - oid: string; - vidx: number; +type Issuer = { + C?: string; + O?: string; + OU?: string; + CN?: string; }; -interface x5cCertificate extends jsrsasign.X509 { - version: number; - foffset: number; - aExtInfo: ExtInfo[]; -} +type Subject = { + C?: string; + O?: string; + OU?: string; + CN?: string; +}; + +const issuerSubjectIDKey: { [key: string]: 'C' | 'O' | 'OU' | 'CN' } = { + '2.5.4.6': 'C', + '2.5.4.10': 'O', + '2.5.4.11': 'OU', + '2.5.4.3': 'CN', +}; /** * Extract PEM certificate info * * @param pemCertificate Result from call to `convertASN1toPEM(x5c[0])` */ -export default function getCertificateInfo(pemCertificate: string): CertificateInfo { - const subjectCert = new X509(); - subjectCert.readCertPEM(pemCertificate); - - // Break apart the Issuer - const issuerString = subjectCert.getIssuerString(); - const issuerParts = issuerString.slice(1).split('/'); +export default function getCertificateInfo(leafCertBuffer: Buffer): CertificateInfo { + const asnx509 = AsnParser.parse(leafCertBuffer, Certificate); + const parsedCert = asnx509.tbsCertificate; - const issuer: { [key: string]: string } = {}; - issuerParts.forEach(field => { - const [key, val] = field.split('='); - issuer[key] = val; - }); - - // Break apart the Subject - let subjectRaw = '/'; - try { - subjectRaw = subjectCert.getSubjectString(); - } catch (err) { - // Don't throw on an error that indicates an empty subject - if (err !== 'malformed RDN') { - throw err; + // Issuer + const issuer: Issuer = {}; + parsedCert.issuer.forEach(([iss]) => { + const key = issuerSubjectIDKey[iss.type]; + if (key) { + issuer[key] = iss.value.toString(); } - } - const subjectParts = subjectRaw.slice(1).split('/'); + }); - const subject: { [key: string]: string } = {}; - subjectParts.forEach(field => { - if (field) { - const [key, val] = field.split('='); - subject[key] = val; + // Subject + const subject: Subject = {}; + parsedCert.subject.forEach(([iss]) => { + const key = issuerSubjectIDKey[iss.type]; + if (key) { + subject[key] = iss.value.toString(); } }); - const { version } = subjectCert as x5cCertificate; let basicConstraintsCA = false; - try { - // TODO: Simplify this when jsrsasign gets updated (see note below). Ideally this is all the - // logic we need to determine `basicConstraintsCA` - basicConstraintsCA = !!subjectCert.getExtBasicConstraints()?.cA; - } catch (err) { - /** - * This is a workaround till jsrsasign's X509.getExtBasicConstraints() can recognize this - * legitimate value. See verifyPacked.test.ts for more context. - */ - // Example error message: "hExtV parse error: 3003010100" - if (`${err.message}`.indexOf('3003010100') >= 0) { - const basicConstraintsInfo = subjectCert.getExtInfo('basicConstraints'); - - if (typeof basicConstraintsInfo === 'object' && basicConstraintsInfo.vidx) { - const hExtV = ASN1HEX.getTLV(subjectCert.hex, basicConstraintsInfo.vidx); - if (hExtV === '3003010100') { - basicConstraintsCA = false; - } else { - throw err; - } + if (parsedCert.extensions) { + // console.log(parsedCert.extensions); + for (const ext of parsedCert.extensions) { + if (ext.extnID === id_ce_basicConstraints) { + const basicConstraints = AsnParser.parse(ext.extnValue, BasicConstraints); + basicConstraintsCA = basicConstraints.cA; } - } else { - throw err; } } return { issuer, subject, - version, + version: parsedCert.version, basicConstraintsCA, - notBefore: zulutodate(subjectCert.getNotBefore()), - notAfter: zulutodate(subjectCert.getNotAfter()), + notBefore: parsedCert.validity.notBefore.getTime(), + notAfter: parsedCert.validity.notAfter.getTime(), }; } |