summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src
diff options
context:
space:
mode:
authorMatthew Miller <matthew@millerti.me>2020-06-24 22:23:03 -0700
committerMatthew Miller <matthew@millerti.me>2020-06-24 22:23:03 -0700
commit653aad63e8c1e4ee8ee7577e00b9636a4ac3a0dd (patch)
tree8e91081c2296499061be06f456a22a5715612b62 /packages/server/src
parent4496371054edbc2709dd0848fcce7aa1e6313242 (diff)
Add validateCertificatePath helper
Diffstat (limited to 'packages/server/src')
-rw-r--r--packages/server/src/helpers/validateCertificatePath.ts55
1 files changed, 55 insertions, 0 deletions
diff --git a/packages/server/src/helpers/validateCertificatePath.ts b/packages/server/src/helpers/validateCertificatePath.ts
new file mode 100644
index 0000000..b1df42e
--- /dev/null
+++ b/packages/server/src/helpers/validateCertificatePath.ts
@@ -0,0 +1,55 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+// ASN1HEX exists in the lib, but not typings, I swear
+// @ts-ignore 2305
+import { KJUR, X509, ASN1HEX } from 'jsrsasign';
+
+const { crypto } = KJUR;
+
+/**
+ * Traverse an array of PEM certificates and ensure they form a proper chain
+ * @param certificates Typically the result of `x5c.map(convertASN1toPEM)`
+ */
+export default function validateCertificatePath(certificates: string[]): boolean {
+ if (new Set(certificates).size !== certificates.length) {
+ throw new Error('Invalid certificate path: found duplicate certificates');
+ }
+
+ // From leaf to root, make sure each cert is issued by the next certificate in the chain
+ for (let i = 0; i < certificates.length; i += 1) {
+ const subjectPem = certificates[i];
+
+ const subjectCert = new X509();
+ subjectCert.readCertPEM(subjectPem);
+
+ let issuerPem = '';
+ if (i + 1 >= certificates.length) {
+ issuerPem = subjectPem;
+ } else {
+ issuerPem = certificates[i + 1];
+ }
+
+ const issuerCert = new X509();
+ issuerCert.readCertPEM(issuerPem);
+
+ console.log('x issuer:', subjectCert.getIssuerString());
+ console.log('x+1 subject:', issuerCert.getSubjectString());
+
+ if (subjectCert.getIssuerString() !== issuerCert.getSubjectString()) {
+ throw new Error('Invalid certificate path: subject issuer did not match issuer subject');
+ }
+
+ const subjectCertStruct = ASN1HEX.getTLVbyList(subjectCert.hex, 0, [0]);
+ const alg = subjectCert.getSignatureAlgorithmField();
+ const signatureHex = subjectCert.getSignatureValueHex();
+
+ const Signature = new crypto.Signature({ alg });
+ Signature.init(issuerPem);
+ Signature.updateHex(subjectCertStruct);
+
+ if (!Signature.verify(signatureHex)) {
+ throw new Error('Invalid certificate path: invalid signature');
+ }
+ }
+
+ return true;
+}