diff --git a/backend/src/server/routes/est/certificate-est-router.ts b/backend/src/server/routes/est/certificate-est-router.ts index 09c33f3a6..1eec348ce 100644 --- a/backend/src/server/routes/est/certificate-est-router.ts +++ b/backend/src/server/routes/est/certificate-est-router.ts @@ -11,7 +11,15 @@ export const registerCertificateEstRouter = async (server: FastifyZodProvider) = // add support for CSR bodies server.addContentTypeParser("application/pkcs10", { parseAs: "string" }, (_, body, done) => { try { - done(null, (body as string).replace(/\n/g, "").replace(/ /g, "")); + let csrBody = body as string; + // some EST clients send CSRs in PEM format and some in base64 format + // for CSRs sent in PEM, we leave them as is + // for CSRs sent in base64, we preprocess them to remove new lines and spaces + if (!csrBody.includes("BEGIN CERTIFICATE REQUEST")) { + csrBody = csrBody.replace(/\n/g, "").replace(/ /g, ""); + } + + done(null, csrBody); } catch (err) { const error = err as Error; done(error, undefined); diff --git a/backend/src/services/certificate-est/certificate-est-fns.ts b/backend/src/services/certificate-est/certificate-est-fns.ts index 91e72e840..a3973ae89 100644 --- a/backend/src/services/certificate-est/certificate-est-fns.ts +++ b/backend/src/services/certificate-est/certificate-est-fns.ts @@ -16,7 +16,9 @@ export const convertRawCertsToPkcs7 = (rawCertificate: ArrayBuffer[]) => { }); const derBuffer = cmsContent.toSchema().toBER(false); - const base64Pkcs7 = Buffer.from(derBuffer).toString("base64"); + const base64Pkcs7 = Buffer.from(derBuffer) + .toString("base64") + .replace(/(.{64})/g, "$1\n"); // we add a linebreak for CURL clients return base64Pkcs7; }; diff --git a/backend/src/services/certificate-est/certificate-est-service.ts b/backend/src/services/certificate-est/certificate-est-service.ts index a2d14d744..e85d4658b 100644 --- a/backend/src/services/certificate-est/certificate-est-service.ts +++ b/backend/src/services/certificate-est/certificate-est-service.ts @@ -158,7 +158,9 @@ export const certificateEstServiceFactory = ({ return new x509.X509Certificate(cert); }); - if (!caCerts) throw new BadRequestError({ message: "Failed to parse certificate chain" }); + if (!caCerts) { + throw new BadRequestError({ message: "Failed to parse certificate chain" }); + } const leafCertificate = decodeURIComponent(sslClientCert).match( /-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g @@ -213,14 +215,11 @@ export const certificateEstServiceFactory = ({ .match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g) ?.map((cert) => new x509.X509Certificate(cert)); - if (!certificates) throw new BadRequestError({ message: "Failed to parse certificate chain" }); - - const caCertificate = new x509.X509Certificate(caCert); - - if (!(await isCertChainValid([caCertificate, ...certificates]))) { - throw new BadRequestError({ message: "Invalid certificate chain" }); + if (!certificates) { + throw new BadRequestError({ message: "Failed to parse certificate chain" }); } + const caCertificate = new x509.X509Certificate(caCert); return convertRawCertsToPkcs7([caCertificate.rawData, ...certificates.map((cert) => cert.rawData)]); }; diff --git a/backend/src/services/certificate/certificate-fns.ts b/backend/src/services/certificate/certificate-fns.ts index 0da09f200..1768a5011 100644 --- a/backend/src/services/certificate/certificate-fns.ts +++ b/backend/src/services/certificate/certificate-fns.ts @@ -30,11 +30,6 @@ export const isCertChainValid = async (certificates: x509.X509Certificate[]) => return true; } - // check for self-signed - if (certificates.length === 2 && certificates[0].equal(certificates[1])) { - return true; - } - const leafCert = certificates[0]; const chain = new x509.X509ChainBuilder({ certificates: certificates.slice(1) diff --git a/docs/documentation/platform/pki/est.mdx b/docs/documentation/platform/pki/est.mdx index 824474d03..aab822dd5 100644 --- a/docs/documentation/platform/pki/est.mdx +++ b/docs/documentation/platform/pki/est.mdx @@ -37,7 +37,7 @@ These endpoints are exposed on port 8443 under the .well-known/est path e.g. - **Certificate Authority Chain** - This is the certificate chain used to validate your devices' manufacturing/pre-installed certificates. This will be used to authenticate your devices with Infisical's EST server. - **Passphrase** - This is also used to authenticate your devices with Infisical's EST server. When configuring the clients, use the value defined here as the EST password. - For security reasons, Infisical authenticates EST clients using both client certificate authentication (against the configured certificate authority chain) and passphrase authentication. + For security reasons, Infisical authenticates EST clients using both client certificate and passphrase. 4. Once the configuration of enrollment options is completed, a new **EST Label** field appears in the enrollment settings. This is the value to use as label in the URL when configuring the connection of EST clients to Infisical. ![est enrollment modal create](/images/platform/pki/est/template-enrollment-est-label.png) @@ -47,3 +47,11 @@ These endpoints are exposed on port 8443 under the .well-known/est path e.g. - https://app.infisical.com:8443/.well-known/est/f110f308-9888-40ab-b228-237b12de8b96/cacerts - https://app.infisical.com:8443/.well-known/est/f110f308-9888-40ab-b228-237b12de8b96/simpleenroll - https://app.infisical.com:8443/.well-known/est/f110f308-9888-40ab-b228-237b12de8b96/simplereenroll + +## Setting up EST clients + +- To use the EST passphrase in your clients, configure it as the EST password. The EST username can be set to any arbitrary value. +- Use the appropriate client certificates for invoking the EST endpoints. + - For `simpleenroll`, use the bootstrapped/manufacturer client certificate. + - For `simplereenroll`, use a valid EST-issued client certificate. +- When configuring the PKCS#12 objects for the client certificates, only include the leaf certificate and the private key. diff --git a/frontend/src/views/Project/CertificatesPage/components/CertificatesTab/components/CertificateTemplateEnrollmentModal.tsx b/frontend/src/views/Project/CertificatesPage/components/CertificatesTab/components/CertificateTemplateEnrollmentModal.tsx index 54a5c5b15..9384cfeec 100644 --- a/frontend/src/views/Project/CertificatesPage/components/CertificatesTab/components/CertificateTemplateEnrollmentModal.tsx +++ b/frontend/src/views/Project/CertificatesPage/components/CertificatesTab/components/CertificateTemplateEnrollmentModal.tsx @@ -3,8 +3,6 @@ import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import z from "zod"; -// import { faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons"; -// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { createNotification } from "@app/components/notifications"; import { Button, @@ -180,7 +178,6 @@ export const CertificateTemplateEnrollmentModal = ({ popUp, handlePopUpToggle }: type={isPassphraseFocused ? "text" : "password"} onFocus={() => setIsPassphraseFocused.on()} onBlur={() => setIsPassphraseFocused.off()} - // rightIcon={} /> )}