From ac26ae389310b610ae6a1d8f65960a5f6461cf19 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Thu, 12 Sep 2024 23:16:49 +0800 Subject: [PATCH] misc: addressed minor cert lint issues --- backend/src/lib/api-docs/constants.ts | 4 ++ .../routes/v1/certificate-authority-router.ts | 28 ++++++++ .../certificate-authority-fns.ts | 2 +- .../certificate-authority-service.ts | 69 +++++++++++++++++-- 4 files changed, 97 insertions(+), 6 deletions(-) diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 7d998f8ea..9735e3f19 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -1073,6 +1073,10 @@ export const CERTIFICATE_AUTHORITIES = { certificateChain: "The certificate chain of the CA", serialNumber: "The serial number of the CA certificate" }, + GET_CERT_BY_ID: { + caId: "The ID of the CA to get the certificate from", + caCertId: "The ID of the CA certificate to get" + }, GET_CA_CERTS: { caId: "The ID of the CA to get the CA certificates for", certificate: "The certificate body of the CA certificate", diff --git a/backend/src/server/routes/v1/certificate-authority-router.ts b/backend/src/server/routes/v1/certificate-authority-router.ts index 77ee70e57..94a203f97 100644 --- a/backend/src/server/routes/v1/certificate-authority-router.ts +++ b/backend/src/server/routes/v1/certificate-authority-router.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ import ms from "ms"; import { z } from "zod"; @@ -139,6 +140,33 @@ export const registerCaRouter = async (server: FastifyZodProvider) => { } }); + // this endpoint will be used to serve the CA certificate when a client makes a request + // against the Authority Information Access CA Issuer URL + server.route({ + method: "GET", + url: "/:caId/certificates/:caCertId", + config: { + rateLimit: readLimit + }, + schema: { + description: "Public endpoint for fetching DER-encoded Certificate of CA", + params: z.object({ + caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CERT_BY_ID.caId), + caCertId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CERT_BY_ID.caCertId) + }), + response: { + 200: z.instanceof(Buffer) + } + }, + handler: async (req, res) => { + const caCert = await server.services.certificateAuthority.getCaCertById(req.params); + + res.header("Content-Type", "application/pkix-cert"); + + return Buffer.from(caCert.rawData); + } + }); + server.route({ method: "PATCH", url: "/:caId", diff --git a/backend/src/services/certificate-authority/certificate-authority-fns.ts b/backend/src/services/certificate-authority/certificate-authority-fns.ts index 7330f029b..65ba57f07 100644 --- a/backend/src/services/certificate-authority/certificate-authority-fns.ts +++ b/backend/src/services/certificate-authority/certificate-authority-fns.ts @@ -15,7 +15,7 @@ import { /* eslint-disable no-bitwise */ export const createSerialNumber = () => { - const randomBytes = crypto.randomBytes(32); + const randomBytes = crypto.randomBytes(20); randomBytes[0] &= 0x7f; // ensure the first bit is 0 return randomBytes.toString("hex"); }; diff --git a/backend/src/services/certificate-authority/certificate-authority-service.ts b/backend/src/services/certificate-authority/certificate-authority-service.ts index 1c2a5a689..12371fd58 100644 --- a/backend/src/services/certificate-authority/certificate-authority-service.ts +++ b/backend/src/services/certificate-authority/certificate-authority-service.ts @@ -762,6 +762,39 @@ export const certificateAuthorityServiceFactory = ({ }; }; + /** + * Return CA certificate object by ID + */ + const getCaCertById = async ({ caId, caCertId }: { caId: string; caCertId: string }) => { + const caCert = await certificateAuthorityCertDAL.findOne({ + caId, + id: caCertId + }); + + if (!caCert) { + throw new NotFoundError({ message: "CA certificate not found" }); + } + + const ca = await certificateAuthorityDAL.findById(caId); + const keyId = await getProjectKmsCertificateKeyId({ + projectId: ca.projectId, + projectDAL, + kmsService + }); + + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: keyId + }); + + const decryptedCaCert = await kmsDecryptor({ + cipherTextBlob: caCert.encryptedCertificate + }); + + const caCertObj = new x509.X509Certificate(decryptedCaCert); + + return caCertObj; + }; + /** * Issue certificate to be imported back in for intermediate CA */ @@ -776,6 +809,7 @@ export const certificateAuthorityServiceFactory = ({ notAfter, maxPathLength }: TSignIntermediateDTO) => { + const appCfg = getConfig(); const ca = await certificateAuthorityDAL.findById(caId); if (!ca) throw new BadRequestError({ message: "CA not found" }); @@ -850,7 +884,7 @@ export const certificateAuthorityServiceFactory = ({ throw new BadRequestError({ message: "notAfter date is after CA certificate's notAfter date" }); } - const { caPrivateKey } = await getCaCredentials({ + const { caPrivateKey, caSecret } = await getCaCredentials({ caId: ca.id, certificateAuthorityDAL, certificateAuthoritySecretDAL, @@ -859,6 +893,11 @@ export const certificateAuthorityServiceFactory = ({ }); const serialNumber = createSerialNumber(); + + const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id }); + const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}`; + + const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}`; const intermediateCert = await x509.X509CertificateGenerator.create({ serialNumber, subject: csrObj.subject, @@ -878,7 +917,11 @@ export const certificateAuthorityServiceFactory = ({ ), new x509.BasicConstraintsExtension(true, maxPathLength === -1 ? undefined : maxPathLength, true), await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false), - await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey) + await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey), + new x509.CRLDistributionPointsExtension([distributionPointUrl]), + new x509.AuthorityInfoAccessExtension({ + caIssuers: new x509.GeneralName("url", caIssuerUrl) + }) ] }); @@ -1169,13 +1212,18 @@ export const certificateAuthorityServiceFactory = ({ const appCfg = getConfig(); const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}`; + const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}`; const extensions: x509.Extension[] = [ new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment, true), new x509.BasicConstraintsExtension(false), new x509.CRLDistributionPointsExtension([distributionPointUrl]), await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false), - await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey) + await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey), + new x509.AuthorityInfoAccessExtension({ + caIssuers: new x509.GeneralName("url", caIssuerUrl) + }), + new x509.CertificatePolicyExtension(["2.5.29.32.0"]) // anyPolicy ]; let altNamesArray: { @@ -1308,6 +1356,7 @@ export const certificateAuthorityServiceFactory = ({ * Note: CSR is generated externally and submitted to Infisical. */ const signCertFromCa = async (dto: TSignCertFromCaDTO) => { + const appCfg = getConfig(); let ca: TCertificateAuthorities | undefined; let certificateTemplate: TCertificateTemplates | undefined; @@ -1432,7 +1481,7 @@ export const certificateAuthorityServiceFactory = ({ message: "A common name (CN) is required in the CSR or as a parameter to this endpoint" }); - const { caPrivateKey } = await getCaCredentials({ + const { caPrivateKey, caSecret } = await getCaCredentials({ caId: ca.id, certificateAuthorityDAL, certificateAuthoritySecretDAL, @@ -1440,11 +1489,20 @@ export const certificateAuthorityServiceFactory = ({ kmsService }); + const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id }); + const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}`; + + const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}`; const extensions: x509.Extension[] = [ new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment, true), new x509.BasicConstraintsExtension(false), await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false), - await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey) + await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey), + new x509.CRLDistributionPointsExtension([distributionPointUrl]), + new x509.AuthorityInfoAccessExtension({ + caIssuers: new x509.GeneralName("url", caIssuerUrl) + }), + new x509.CertificatePolicyExtension(["2.5.29.32.0"]) // anyPolicy ]; let altNamesFromCsr: string = ""; @@ -1628,6 +1686,7 @@ export const certificateAuthorityServiceFactory = ({ renewCaCert, getCaCerts, getCaCert, + getCaCertById, signIntermediate, importCertToCa, issueCertFromCa,