feat: finished up EST cacerts

This commit is contained in:
Sheen Capadngan
2024-08-22 15:39:53 +08:00
parent 760a1e917a
commit b7caff88cf
4 changed files with 100 additions and 33 deletions

View File

@@ -2,7 +2,7 @@ import bcrypt from "bcrypt";
import { z } from "zod";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { writeLimit } from "@app/server/config/rateLimiter";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
export const registerCertificateEstRouter = async (server: FastifyZodProvider) => {
// add support for CSR bodies
@@ -18,6 +18,13 @@ export const registerCertificateEstRouter = async (server: FastifyZodProvider) =
// Authenticate EST client using Passphrase
server.addHook("onRequest", async (req, res) => {
const { authorization } = req.headers;
const urlFragments = req.url.split("/");
// cacerts endpoint should not have any authentication
if (urlFragments[urlFragments.length - 1] === "cacerts") {
return;
}
if (!authorization) {
const wwwAuthenticateHeader = "WWW-Authenticate";
const errAuthRequired = "Authentication required";
@@ -36,7 +43,6 @@ export const registerCertificateEstRouter = async (server: FastifyZodProvider) =
return;
}
const urlFragments = req.url.split("/");
const certificateTemplateId = urlFragments.slice(-2)[0];
const estConfig = await server.services.certificateTemplate.getEstConfiguration({
isInternal: true,
@@ -124,4 +130,28 @@ export const registerCertificateEstRouter = async (server: FastifyZodProvider) =
});
}
});
server.route({
method: "GET",
url: "/:certificateTemplateId/cacerts",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
certificateTemplateId: z.string().min(1)
}),
response: {
200: z.string()
}
},
handler: async (req, res) => {
void res.header("Content-Type", "application/pkcs7-mime; smime-type=certs-only");
void res.header("Content-Transfer-Encoding", "base64");
return server.services.certificateEst.getCaCerts({
certificateTemplateId: req.params.certificateTemplateId
});
}
});
};

View File

@@ -1,13 +1,12 @@
import { X509Certificate } from "@peculiar/x509";
import { Certificate, ContentInfo, EncapsulatedContentInfo, SignedData } from "pkijs";
export const convertRawCertToPkcs7 = (rawCertificate: ArrayBuffer) => {
const cert = Certificate.fromBER(rawCertificate);
export const convertRawCertsToPkcs7 = (rawCertificate: ArrayBuffer[]) => {
const certs = rawCertificate.map((rawCert) => Certificate.fromBER(rawCert));
const cmsSigned = new SignedData({
encapContentInfo: new EncapsulatedContentInfo({
eContentType: "1.2.840.113549.1.7.1" // not encrypted and not compressed data
}),
certificates: [cert]
certificates: certs
});
const cmsContent = new ContentInfo({
@@ -21,22 +20,3 @@ export const convertRawCertToPkcs7 = (rawCertificate: ArrayBuffer) => {
return base64Pkcs7;
};
export const checkCertValidityAgainstChain = async (cert: X509Certificate, chainCerts: X509Certificate[]) => {
let isSslClientCertValid = true;
let certToVerify = cert;
for await (const issuerCert of chainCerts) {
if (
await certToVerify.verify({
publicKey: issuerCert.publicKey
})
) {
certToVerify = issuerCert; // Move to the next certificate in the chain
} else {
isSslClientCertValid = false;
}
}
return isSslClientCertValid;
};

View File

@@ -1,23 +1,24 @@
import * as x509 from "@peculiar/x509";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { BadRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { checkCertValidityAgainstChain, convertCertPemToRaw } from "../certificate/certificate-fns";
import { TCertificateAuthorityCertDALFactory } from "../certificate-authority/certificate-authority-cert-dal";
import { TCertificateAuthorityDALFactory } from "../certificate-authority/certificate-authority-dal";
import { getCaCertChains } from "../certificate-authority/certificate-authority-fns";
import { getCaCertChain, getCaCertChains } from "../certificate-authority/certificate-authority-fns";
import { TCertificateAuthorityServiceFactory } from "../certificate-authority/certificate-authority-service";
import { TCertificateTemplateDALFactory } from "../certificate-template/certificate-template-dal";
import { TCertificateTemplateServiceFactory } from "../certificate-template/certificate-template-service";
import { TKmsServiceFactory } from "../kms/kms-service";
import { TProjectDALFactory } from "../project/project-dal";
import { checkCertValidityAgainstChain, convertRawCertToPkcs7 } from "./certificate-est-fns";
import { convertRawCertsToPkcs7 } from "./certificate-est-fns";
type TCertificateEstServiceFactoryDep = {
certificateAuthorityService: Pick<TCertificateAuthorityServiceFactory, "signCertFromCa">;
certificateTemplateService: Pick<TCertificateTemplateServiceFactory, "getEstConfiguration" | "getCertTemplate">;
certificateTemplateService: Pick<TCertificateTemplateServiceFactory, "getEstConfiguration">;
certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "findById">;
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "find">;
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "find" | "findById">;
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
};
@@ -155,7 +156,7 @@ export const certificateEstServiceFactory = ({
csr
});
return convertRawCertToPkcs7(rawCertificate);
return convertRawCertsToPkcs7([rawCertificate]);
};
const simpleEnroll = async ({
@@ -228,10 +229,43 @@ export const certificateEstServiceFactory = ({
csr
});
return convertRawCertToPkcs7(rawCertificate);
return convertRawCertsToPkcs7([rawCertificate]);
};
const getCaCerts = async ({ certificateTemplateId }: { certificateTemplateId: string }) => {
const certTemplate = await certificateTemplateDAL.findById(certificateTemplateId);
if (!certTemplate) {
throw new NotFoundError({
message: "Certificate template not found"
});
}
const ca = await certificateAuthorityDAL.findById(certTemplate.caId);
if (!ca) {
throw new NotFoundError({
message: "Certificate Authority not found"
});
}
const { caCert, caCertChain } = await getCaCertChain({
caCertId: ca.activeCaCertId as string,
certificateAuthorityDAL,
certificateAuthorityCertDAL,
projectDAL,
kmsService
});
const caCertRaw = convertCertPemToRaw(caCert);
const caParentsRaw = caCertChain
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
?.map(convertCertPemToRaw);
return convertRawCertsToPkcs7([caCertRaw, ...(caParentsRaw ?? [])]);
};
return {
simpleEnroll,
simpleReenroll
simpleReenroll,
getCaCerts
};
};

View File

@@ -24,3 +24,26 @@ export const revocationReasonToCrlCode = (crlReason: CrlReason) => {
return x509.X509CrlReason.unspecified;
}
};
export const convertCertPemToRaw = (certPem: string) => {
return new x509.X509Certificate(certPem).rawData;
};
export const checkCertValidityAgainstChain = async (cert: x509.X509Certificate, chainCerts: x509.X509Certificate[]) => {
let isSslClientCertValid = true;
let certToVerify = cert;
for await (const issuerCert of chainCerts) {
if (
await certToVerify.verify({
publicKey: issuerCert.publicKey
})
) {
certToVerify = issuerCert; // Move to the next certificate in the chain
} else {
isSslClientCertValid = false;
}
}
return isSslClientCertValid;
};