mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 16:27:46 +00:00
feat: finished up EST cacerts
This commit is contained in:
@@ -2,7 +2,7 @@ import bcrypt from "bcrypt";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
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) => {
|
export const registerCertificateEstRouter = async (server: FastifyZodProvider) => {
|
||||||
// add support for CSR bodies
|
// add support for CSR bodies
|
||||||
@@ -18,6 +18,13 @@ export const registerCertificateEstRouter = async (server: FastifyZodProvider) =
|
|||||||
// Authenticate EST client using Passphrase
|
// Authenticate EST client using Passphrase
|
||||||
server.addHook("onRequest", async (req, res) => {
|
server.addHook("onRequest", async (req, res) => {
|
||||||
const { authorization } = req.headers;
|
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) {
|
if (!authorization) {
|
||||||
const wwwAuthenticateHeader = "WWW-Authenticate";
|
const wwwAuthenticateHeader = "WWW-Authenticate";
|
||||||
const errAuthRequired = "Authentication required";
|
const errAuthRequired = "Authentication required";
|
||||||
@@ -36,7 +43,6 @@ export const registerCertificateEstRouter = async (server: FastifyZodProvider) =
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlFragments = req.url.split("/");
|
|
||||||
const certificateTemplateId = urlFragments.slice(-2)[0];
|
const certificateTemplateId = urlFragments.slice(-2)[0];
|
||||||
const estConfig = await server.services.certificateTemplate.getEstConfiguration({
|
const estConfig = await server.services.certificateTemplate.getEstConfiguration({
|
||||||
isInternal: true,
|
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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { X509Certificate } from "@peculiar/x509";
|
|
||||||
import { Certificate, ContentInfo, EncapsulatedContentInfo, SignedData } from "pkijs";
|
import { Certificate, ContentInfo, EncapsulatedContentInfo, SignedData } from "pkijs";
|
||||||
|
|
||||||
export const convertRawCertToPkcs7 = (rawCertificate: ArrayBuffer) => {
|
export const convertRawCertsToPkcs7 = (rawCertificate: ArrayBuffer[]) => {
|
||||||
const cert = Certificate.fromBER(rawCertificate);
|
const certs = rawCertificate.map((rawCert) => Certificate.fromBER(rawCert));
|
||||||
const cmsSigned = new SignedData({
|
const cmsSigned = new SignedData({
|
||||||
encapContentInfo: new EncapsulatedContentInfo({
|
encapContentInfo: new EncapsulatedContentInfo({
|
||||||
eContentType: "1.2.840.113549.1.7.1" // not encrypted and not compressed data
|
eContentType: "1.2.840.113549.1.7.1" // not encrypted and not compressed data
|
||||||
}),
|
}),
|
||||||
certificates: [cert]
|
certificates: certs
|
||||||
});
|
});
|
||||||
|
|
||||||
const cmsContent = new ContentInfo({
|
const cmsContent = new ContentInfo({
|
||||||
@@ -21,22 +20,3 @@ export const convertRawCertToPkcs7 = (rawCertificate: ArrayBuffer) => {
|
|||||||
|
|
||||||
return base64Pkcs7;
|
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;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import * as x509 from "@peculiar/x509";
|
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 { TCertificateAuthorityCertDALFactory } from "../certificate-authority/certificate-authority-cert-dal";
|
||||||
import { TCertificateAuthorityDALFactory } from "../certificate-authority/certificate-authority-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 { TCertificateAuthorityServiceFactory } from "../certificate-authority/certificate-authority-service";
|
||||||
import { TCertificateTemplateDALFactory } from "../certificate-template/certificate-template-dal";
|
import { TCertificateTemplateDALFactory } from "../certificate-template/certificate-template-dal";
|
||||||
import { TCertificateTemplateServiceFactory } from "../certificate-template/certificate-template-service";
|
import { TCertificateTemplateServiceFactory } from "../certificate-template/certificate-template-service";
|
||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
import { TProjectDALFactory } from "../project/project-dal";
|
import { TProjectDALFactory } from "../project/project-dal";
|
||||||
import { checkCertValidityAgainstChain, convertRawCertToPkcs7 } from "./certificate-est-fns";
|
import { convertRawCertsToPkcs7 } from "./certificate-est-fns";
|
||||||
|
|
||||||
type TCertificateEstServiceFactoryDep = {
|
type TCertificateEstServiceFactoryDep = {
|
||||||
certificateAuthorityService: Pick<TCertificateAuthorityServiceFactory, "signCertFromCa">;
|
certificateAuthorityService: Pick<TCertificateAuthorityServiceFactory, "signCertFromCa">;
|
||||||
certificateTemplateService: Pick<TCertificateTemplateServiceFactory, "getEstConfiguration" | "getCertTemplate">;
|
certificateTemplateService: Pick<TCertificateTemplateServiceFactory, "getEstConfiguration">;
|
||||||
certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "findById">;
|
certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "findById">;
|
||||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "find">;
|
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "find" | "findById">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||||
};
|
};
|
||||||
@@ -155,7 +156,7 @@ export const certificateEstServiceFactory = ({
|
|||||||
csr
|
csr
|
||||||
});
|
});
|
||||||
|
|
||||||
return convertRawCertToPkcs7(rawCertificate);
|
return convertRawCertsToPkcs7([rawCertificate]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const simpleEnroll = async ({
|
const simpleEnroll = async ({
|
||||||
@@ -228,10 +229,43 @@ export const certificateEstServiceFactory = ({
|
|||||||
csr
|
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 {
|
return {
|
||||||
simpleEnroll,
|
simpleEnroll,
|
||||||
simpleReenroll
|
simpleReenroll,
|
||||||
|
getCaCerts
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,3 +24,26 @@ export const revocationReasonToCrlCode = (crlReason: CrlReason) => {
|
|||||||
return x509.X509CrlReason.unspecified;
|
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;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user