mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 07:28:21 +00:00
Modularized repetitive ca + chain logic
This commit is contained in:
@@ -5,7 +5,7 @@ import { BadRequestError } from "@app/lib/errors";
|
|||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|
||||||
import { CertKeyAlgorithm, CertStatus } from "../certificate/certificate-types";
|
import { CertKeyAlgorithm, CertStatus } from "../certificate/certificate-types";
|
||||||
import { TDNParts, TRebuildCaCrlDTO } from "./certificate-authority-types";
|
import { TDNParts, TGetCaCertChainDTO, TGetCaCredentialsDTO, TRebuildCaCrlDTO } from "./certificate-authority-types";
|
||||||
|
|
||||||
export const createDistinguishedName = (parts: TDNParts) => {
|
export const createDistinguishedName = (parts: TDNParts) => {
|
||||||
const dnParts = [];
|
const dnParts = [];
|
||||||
@@ -51,6 +51,101 @@ export const keyAlgorithmToAlgCfg = (keyAlgorithm: CertKeyAlgorithm) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the public and private key of CA with id [caId]
|
||||||
|
* Note: credentials are returned as crypto.webcrypto.CryptoKey
|
||||||
|
* suitable for use with @peculiar/x509 module
|
||||||
|
*/
|
||||||
|
export const getCaCredentials = async ({
|
||||||
|
caId,
|
||||||
|
certificateAuthorityDAL,
|
||||||
|
certificateAuthoritySecretDAL,
|
||||||
|
projectDAL,
|
||||||
|
kmsService
|
||||||
|
}: TGetCaCredentialsDTO) => {
|
||||||
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
|
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||||
|
|
||||||
|
const caSecret = await certificateAuthoritySecretDAL.findOne({ caId });
|
||||||
|
if (!caSecret) throw new BadRequestError({ message: "CA secret not found" });
|
||||||
|
|
||||||
|
const keyId = await getProjectKmsCertificateKeyId({
|
||||||
|
projectId: ca.projectId,
|
||||||
|
projectDAL,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
|
const decryptedPrivateKey = await kmsService.decrypt({
|
||||||
|
kmsId: keyId,
|
||||||
|
cipherTextBlob: caSecret.encryptedPrivateKey
|
||||||
|
});
|
||||||
|
|
||||||
|
const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm);
|
||||||
|
const skObj = crypto.createPrivateKey({ key: decryptedPrivateKey, format: "der", type: "pkcs8" });
|
||||||
|
const caPrivateKey = await crypto.subtle.importKey(
|
||||||
|
"pkcs8",
|
||||||
|
skObj.export({ format: "der", type: "pkcs8" }),
|
||||||
|
alg,
|
||||||
|
true,
|
||||||
|
["sign"]
|
||||||
|
);
|
||||||
|
|
||||||
|
const pkObj = crypto.createPublicKey(skObj);
|
||||||
|
const caPublicKey = await crypto.subtle.importKey("spki", pkObj.export({ format: "der", type: "spki" }), alg, true, [
|
||||||
|
"verify"
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
caPrivateKey,
|
||||||
|
caPublicKey
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the decrypted pem-encoded certificate and certificate chain
|
||||||
|
* for CA with id [caId].
|
||||||
|
*/
|
||||||
|
export const getCaCertChain = async ({
|
||||||
|
caId,
|
||||||
|
certificateAuthorityDAL,
|
||||||
|
certificateAuthorityCertDAL,
|
||||||
|
projectDAL,
|
||||||
|
kmsService
|
||||||
|
}: TGetCaCertChainDTO) => {
|
||||||
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
|
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||||
|
|
||||||
|
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
||||||
|
|
||||||
|
const keyId = await getProjectKmsCertificateKeyId({
|
||||||
|
projectId: ca.projectId,
|
||||||
|
projectDAL,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
|
const decryptedCaCert = await kmsService.decrypt({
|
||||||
|
kmsId: keyId,
|
||||||
|
cipherTextBlob: caCert.encryptedCertificate
|
||||||
|
});
|
||||||
|
|
||||||
|
const caCertObj = new x509.X509Certificate(decryptedCaCert);
|
||||||
|
|
||||||
|
const decryptedChain = await kmsService.decrypt({
|
||||||
|
kmsId: keyId,
|
||||||
|
cipherTextBlob: caCert.encryptedCertificateChain
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
caCert: caCertObj.toString("pem"),
|
||||||
|
caCertChain: decryptedChain.toString("utf-8"),
|
||||||
|
serialNumber: caCertObj.serialNumber
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuilds the certificate revocation list (CRL)
|
||||||
|
* for CA with id [caId]
|
||||||
|
*/
|
||||||
export const rebuildCaCrl = async ({
|
export const rebuildCaCrl = async ({
|
||||||
caId,
|
caId,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
|
|||||||
@@ -16,7 +16,12 @@ import { CertKeyAlgorithm, CertStatus } from "../certificate/certificate-types";
|
|||||||
import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal";
|
import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal";
|
||||||
import { TCertificateAuthorityCrlDALFactory } from "./certificate-authority-crl-dal";
|
import { TCertificateAuthorityCrlDALFactory } from "./certificate-authority-crl-dal";
|
||||||
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
||||||
import { createDistinguishedName, keyAlgorithmToAlgCfg } from "./certificate-authority-fns";
|
import {
|
||||||
|
createDistinguishedName,
|
||||||
|
getCaCertChain,
|
||||||
|
getCaCredentials,
|
||||||
|
keyAlgorithmToAlgCfg
|
||||||
|
} from "./certificate-authority-fns";
|
||||||
import { TCertificateAuthorityQueueFactory } from "./certificate-authority-queue";
|
import { TCertificateAuthorityQueueFactory } from "./certificate-authority-queue";
|
||||||
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
|
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
|
||||||
import {
|
import {
|
||||||
@@ -65,7 +70,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
permissionService
|
permissionService
|
||||||
}: TCertificateAuthorityServiceFactoryDep) => {
|
}: TCertificateAuthorityServiceFactoryDep) => {
|
||||||
/**
|
/**
|
||||||
* Generates a new root or intermediate CA
|
* Generates new root or intermediate CA
|
||||||
*/
|
*/
|
||||||
const createCa = async ({
|
const createCa = async ({
|
||||||
projectSlug,
|
projectSlug,
|
||||||
@@ -237,6 +242,9 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
return newCa;
|
return newCa;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return CA with id [caId]
|
||||||
|
*/
|
||||||
const getCaById = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaDTO) => {
|
const getCaById = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaDTO) => {
|
||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||||
@@ -256,6 +264,10 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
return ca;
|
return ca;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update CA with id [caId].
|
||||||
|
* Note: Used to enable/disable CA
|
||||||
|
*/
|
||||||
const updateCaById = async ({ caId, status, actorId, actorAuthMethod, actor, actorOrgId }: TUpdateCaDTO) => {
|
const updateCaById = async ({ caId, status, actorId, actorAuthMethod, actor, actorOrgId }: TUpdateCaDTO) => {
|
||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||||
@@ -278,6 +290,9 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
return updatedCa;
|
return updatedCa;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete CA with id [caId]
|
||||||
|
*/
|
||||||
const deleteCaById = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TDeleteCaDTO) => {
|
const deleteCaById = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TDeleteCaDTO) => {
|
||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||||
@@ -301,7 +316,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a CSR for a CA
|
* Return certificate signing request (CSR) made with CA with id [caId]
|
||||||
*/
|
*/
|
||||||
const getCaCsr = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaCsrDTO) => {
|
const getCaCsr = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaCsrDTO) => {
|
||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
@@ -325,33 +340,22 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
||||||
if (caCert) throw new BadRequestError({ message: "CA already has a certificate installed" });
|
if (caCert) throw new BadRequestError({ message: "CA already has a certificate installed" });
|
||||||
|
|
||||||
const caSecret = await certificateAuthoritySecretDAL.findOne({ caId: ca.id });
|
const { caPrivateKey, caPublicKey } = await getCaCredentials({
|
||||||
|
caId,
|
||||||
const keyId = await getProjectKmsCertificateKeyId({
|
certificateAuthorityDAL,
|
||||||
projectId: ca.projectId,
|
certificateAuthoritySecretDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
const privateKey = await kmsService.decrypt({
|
|
||||||
kmsId: keyId,
|
|
||||||
cipherTextBlob: caSecret.encryptedPrivateKey
|
|
||||||
});
|
|
||||||
|
|
||||||
const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm);
|
const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm);
|
||||||
const skObj = crypto.createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
|
|
||||||
const sk = await crypto.subtle.importKey("pkcs8", skObj.export({ format: "der", type: "pkcs8" }), alg, true, [
|
|
||||||
"sign"
|
|
||||||
]);
|
|
||||||
const pkObj = crypto.createPublicKey(skObj);
|
|
||||||
|
|
||||||
const pk = await crypto.subtle.importKey("spki", pkObj.export({ format: "der", type: "spki" }), alg, true, [
|
|
||||||
"verify"
|
|
||||||
]);
|
|
||||||
|
|
||||||
const csrObj = await x509.Pkcs10CertificateRequestGenerator.create({
|
const csrObj = await x509.Pkcs10CertificateRequestGenerator.create({
|
||||||
name: ca.dn,
|
name: ca.dn,
|
||||||
keys: { privateKey: sk, publicKey: pk },
|
keys: {
|
||||||
|
privateKey: caPrivateKey,
|
||||||
|
publicKey: caPublicKey
|
||||||
|
},
|
||||||
signingAlgorithm: alg,
|
signingAlgorithm: alg,
|
||||||
extensions: [
|
extensions: [
|
||||||
// eslint-disable-next-line no-bitwise
|
// eslint-disable-next-line no-bitwise
|
||||||
@@ -388,30 +392,18 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
ProjectPermissionSub.CertificateAuthorities
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
);
|
);
|
||||||
|
|
||||||
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
const { caCert, caCertChain, serialNumber } = await getCaCertChain({
|
||||||
|
caId,
|
||||||
const keyId = await getProjectKmsCertificateKeyId({
|
certificateAuthorityDAL,
|
||||||
projectId: ca.projectId,
|
certificateAuthorityCertDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
const decryptedCaCert = await kmsService.decrypt({
|
|
||||||
kmsId: keyId,
|
|
||||||
cipherTextBlob: caCert.encryptedCertificate
|
|
||||||
});
|
|
||||||
|
|
||||||
const certObj = new x509.X509Certificate(decryptedCaCert);
|
|
||||||
|
|
||||||
const decryptedChain = await kmsService.decrypt({
|
|
||||||
kmsId: keyId,
|
|
||||||
cipherTextBlob: caCert.encryptedCertificateChain
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
certificate: certObj.toString("pem"),
|
certificate: caCert,
|
||||||
certificateChain: decryptedChain.toString("utf-8"),
|
certificateChain: caCertChain,
|
||||||
serialNumber: certObj.serialNumber
|
serialNumber
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -456,18 +448,6 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
||||||
const caSecret = await certificateAuthoritySecretDAL.findOne({ caId: ca.id });
|
|
||||||
|
|
||||||
const privateKey = await kmsService.decrypt({
|
|
||||||
kmsId: keyId,
|
|
||||||
cipherTextBlob: caSecret.encryptedPrivateKey
|
|
||||||
});
|
|
||||||
|
|
||||||
const skObj = crypto.createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
|
|
||||||
const sk = await crypto.subtle.importKey("pkcs8", skObj.export({ format: "der", type: "pkcs8" }), alg, true, [
|
|
||||||
"sign"
|
|
||||||
]);
|
|
||||||
|
|
||||||
const decryptedCaCert = await kmsService.decrypt({
|
const decryptedCaCert = await kmsService.decrypt({
|
||||||
kmsId: keyId,
|
kmsId: keyId,
|
||||||
cipherTextBlob: caCert.encryptedCertificate
|
cipherTextBlob: caCert.encryptedCertificate
|
||||||
@@ -507,6 +487,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
throw new BadRequestError({ message: "notAfter date is after CA certificate's notAfter date" });
|
throw new BadRequestError({ message: "notAfter date is after CA certificate's notAfter date" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { caPrivateKey } = await getCaCredentials({
|
||||||
|
caId: ca.id,
|
||||||
|
certificateAuthorityDAL,
|
||||||
|
certificateAuthoritySecretDAL,
|
||||||
|
projectDAL,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
const serialNumber = crypto.randomBytes(32).toString("hex");
|
const serialNumber = crypto.randomBytes(32).toString("hex");
|
||||||
const intermediateCert = await x509.X509CertificateGenerator.create({
|
const intermediateCert = await x509.X509CertificateGenerator.create({
|
||||||
serialNumber,
|
serialNumber,
|
||||||
@@ -514,7 +502,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
issuer: caCertObj.subject,
|
issuer: caCertObj.subject,
|
||||||
notBefore: notBeforeDate,
|
notBefore: notBeforeDate,
|
||||||
notAfter: notAfterDate,
|
notAfter: notAfterDate,
|
||||||
signingKey: sk,
|
signingKey: caPrivateKey,
|
||||||
publicKey: csrObj.publicKey,
|
publicKey: csrObj.publicKey,
|
||||||
signingAlgorithm: alg,
|
signingAlgorithm: alg,
|
||||||
extensions: [
|
extensions: [
|
||||||
@@ -531,21 +519,27 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const caCertChain = await kmsService.decrypt({
|
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||||
kmsId: keyId,
|
caId,
|
||||||
cipherTextBlob: caCert.encryptedCertificateChain
|
certificateAuthorityDAL,
|
||||||
|
certificateAuthorityCertDAL,
|
||||||
|
projectDAL,
|
||||||
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
const certificateChain = `${caCertObj.toString("pem")}\n${caCertChain.toString("utf-8")}`.trim();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
certificate: intermediateCert.toString("pem"),
|
certificate: intermediateCert.toString("pem"),
|
||||||
issuingCaCertificate: caCertObj.toString("pem"),
|
issuingCaCertificate,
|
||||||
certificateChain,
|
certificateChain: `${issuingCaCertificate}\n${caCertChain}`.trim(),
|
||||||
serialNumber: intermediateCert.serialNumber
|
serialNumber: intermediateCert.serialNumber
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import certificate for (un-installed) CA with id [caId].
|
||||||
|
* Note: Can be used to import an external certificate and certificate chain
|
||||||
|
* to be installed into the CA.
|
||||||
|
*/
|
||||||
const importCertToCa = async ({
|
const importCertToCa = async ({
|
||||||
caId,
|
caId,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -643,6 +637,9 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return new leaf certificate issued by CA with id [caId]
|
||||||
|
*/
|
||||||
const issueCertFromCa = async ({
|
const issueCertFromCa = async ({
|
||||||
caId,
|
caId,
|
||||||
commonName,
|
commonName,
|
||||||
@@ -685,33 +682,6 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
|
|
||||||
const caCertObj = new x509.X509Certificate(decryptedCaCert);
|
const caCertObj = new x509.X509Certificate(decryptedCaCert);
|
||||||
|
|
||||||
const caSecret = await certificateAuthoritySecretDAL.findOne({ caId: ca.id });
|
|
||||||
|
|
||||||
const privateKey = await kmsService.decrypt({
|
|
||||||
kmsId: keyId,
|
|
||||||
cipherTextBlob: caSecret.encryptedPrivateKey
|
|
||||||
});
|
|
||||||
|
|
||||||
const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm);
|
|
||||||
|
|
||||||
const caSkObj = crypto.createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
|
|
||||||
const caSk = await crypto.subtle.importKey("pkcs8", caSkObj.export({ format: "der", type: "pkcs8" }), alg, true, [
|
|
||||||
"sign"
|
|
||||||
]);
|
|
||||||
|
|
||||||
const leafKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
|
||||||
|
|
||||||
const csrObj = await x509.Pkcs10CertificateRequestGenerator.create({
|
|
||||||
name: `CN=${commonName}`,
|
|
||||||
keys: leafKeys,
|
|
||||||
signingAlgorithm: alg,
|
|
||||||
extensions: [
|
|
||||||
// eslint-disable-next-line no-bitwise
|
|
||||||
new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment)
|
|
||||||
],
|
|
||||||
attributes: [new x509.ChallengePasswordAttribute("password")]
|
|
||||||
});
|
|
||||||
|
|
||||||
const notBeforeDate = notBefore ? new Date(notBefore) : new Date();
|
const notBeforeDate = notBefore ? new Date(notBefore) : new Date();
|
||||||
|
|
||||||
let notAfterDate = new Date(new Date().setFullYear(new Date().getFullYear() + 1));
|
let notAfterDate = new Date(new Date().setFullYear(new Date().getFullYear() + 1));
|
||||||
@@ -737,6 +707,28 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
throw new BadRequestError({ message: "notAfter date is after CA certificate's notAfter date" });
|
throw new BadRequestError({ message: "notAfter date is after CA certificate's notAfter date" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm);
|
||||||
|
const leafKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
||||||
|
|
||||||
|
const csrObj = await x509.Pkcs10CertificateRequestGenerator.create({
|
||||||
|
name: `CN=${commonName}`,
|
||||||
|
keys: leafKeys,
|
||||||
|
signingAlgorithm: alg,
|
||||||
|
extensions: [
|
||||||
|
// eslint-disable-next-line no-bitwise
|
||||||
|
new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment)
|
||||||
|
],
|
||||||
|
attributes: [new x509.ChallengePasswordAttribute("password")]
|
||||||
|
});
|
||||||
|
|
||||||
|
const { caPrivateKey } = await getCaCredentials({
|
||||||
|
caId: ca.id,
|
||||||
|
certificateAuthorityDAL,
|
||||||
|
certificateAuthoritySecretDAL,
|
||||||
|
projectDAL,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
const serialNumber = crypto.randomBytes(32).toString("hex");
|
const serialNumber = crypto.randomBytes(32).toString("hex");
|
||||||
const leafCert = await x509.X509CertificateGenerator.create({
|
const leafCert = await x509.X509CertificateGenerator.create({
|
||||||
serialNumber,
|
serialNumber,
|
||||||
@@ -744,7 +736,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
issuer: caCertObj.subject,
|
issuer: caCertObj.subject,
|
||||||
notBefore: notBeforeDate,
|
notBefore: notBeforeDate,
|
||||||
notAfter: notAfterDate,
|
notAfter: notAfterDate,
|
||||||
signingKey: caSk,
|
signingKey: caPrivateKey,
|
||||||
publicKey: csrObj.publicKey,
|
publicKey: csrObj.publicKey,
|
||||||
signingAlgorithm: alg,
|
signingAlgorithm: alg,
|
||||||
extensions: [
|
extensions: [
|
||||||
@@ -763,11 +755,6 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
plainText: Buffer.from(new Uint8Array(leafCert.rawData))
|
plainText: Buffer.from(new Uint8Array(leafCert.rawData))
|
||||||
});
|
});
|
||||||
|
|
||||||
const caCertChain = await kmsService.decrypt({
|
|
||||||
kmsId: keyId,
|
|
||||||
cipherTextBlob: caCert.encryptedCertificateChain
|
|
||||||
});
|
|
||||||
|
|
||||||
await certificateDAL.transaction(async (tx) => {
|
await certificateDAL.transaction(async (tx) => {
|
||||||
const cert = await certificateDAL.create(
|
const cert = await certificateDAL.create(
|
||||||
{
|
{
|
||||||
@@ -792,19 +779,25 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
return cert;
|
return cert;
|
||||||
});
|
});
|
||||||
|
|
||||||
const certificateChain = `${caCertObj.toString("pem")}\n${caCertChain.toString("utf-8")}`.trim();
|
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||||
|
caId: ca.id,
|
||||||
|
certificateAuthorityDAL,
|
||||||
|
certificateAuthorityCertDAL,
|
||||||
|
projectDAL,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
certificate: leafCert.toString("pem"),
|
certificate: leafCert.toString("pem"),
|
||||||
certificateChain,
|
certificateChain: `${issuingCaCertificate}\n${caCertChain}`.trim(),
|
||||||
issuingCaCertificate: caCertObj.toString("pem"),
|
issuingCaCertificate,
|
||||||
privateKey: skLeaf,
|
privateKey: skLeaf,
|
||||||
serialNumber
|
serialNumber
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Certificate Revocation List (CRL) for the CA
|
* Return the Certificate Revocation List (CRL) for CA with id [caId]
|
||||||
*/
|
*/
|
||||||
const getCaCrl = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCrl) => {
|
const getCaCrl = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCrl) => {
|
||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
|||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
import { CertKeyAlgorithm } from "../certificate/certificate-types";
|
import { CertKeyAlgorithm } from "../certificate/certificate-types";
|
||||||
|
import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal";
|
||||||
import { TCertificateAuthorityCrlDALFactory } from "./certificate-authority-crl-dal";
|
import { TCertificateAuthorityCrlDALFactory } from "./certificate-authority-crl-dal";
|
||||||
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
||||||
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
|
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
|
||||||
@@ -94,6 +95,22 @@ export type TDNParts = {
|
|||||||
locality?: string;
|
locality?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TGetCaCredentialsDTO = {
|
||||||
|
caId: string;
|
||||||
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||||
|
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
|
||||||
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||||
|
kmsService: Pick<TKmsServiceFactory, "decrypt" | "generateKmsKey">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetCaCertChainDTO = {
|
||||||
|
caId: string;
|
||||||
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||||
|
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findOne">;
|
||||||
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||||
|
kmsService: Pick<TKmsServiceFactory, "decrypt" | "generateKmsKey">;
|
||||||
|
};
|
||||||
|
|
||||||
export type TRebuildCaCrlDTO = {
|
export type TRebuildCaCrlDTO = {
|
||||||
caId: string;
|
caId: string;
|
||||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
|||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|
||||||
import { rebuildCaCrl } from "../certificate-authority/certificate-authority-fns";
|
import { getCaCertChain, rebuildCaCrl } from "../certificate-authority/certificate-authority-fns";
|
||||||
import { revocationReasonToCrlCode } from "./certificate-fns";
|
import { revocationReasonToCrlCode } from "./certificate-fns";
|
||||||
import { CertStatus, TDeleteCertDTO, TGetCertCertDTO, TGetCertDTO, TRevokeCertDTO } from "./certificate-types";
|
import { CertStatus, TDeleteCertDTO, TGetCertCertDTO, TGetCertDTO, TRevokeCertDTO } from "./certificate-types";
|
||||||
|
|
||||||
@@ -42,6 +42,9 @@ export const certificateServiceFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
permissionService
|
permissionService
|
||||||
}: TCertificateServiceFactoryDep) => {
|
}: TCertificateServiceFactoryDep) => {
|
||||||
|
/**
|
||||||
|
* Return details for certificate with serial number [serialNumber]
|
||||||
|
*/
|
||||||
const getCert = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TGetCertDTO) => {
|
const getCert = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TGetCertDTO) => {
|
||||||
const cert = await certificateDAL.findOne({ serialNumber });
|
const cert = await certificateDAL.findOne({ serialNumber });
|
||||||
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
||||||
@@ -59,6 +62,9 @@ export const certificateServiceFactory = ({
|
|||||||
return cert;
|
return cert;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete certificate with serial number [serialNumber]
|
||||||
|
*/
|
||||||
const deleteCert = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TDeleteCertDTO) => {
|
const deleteCert = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TDeleteCertDTO) => {
|
||||||
const cert = await certificateDAL.findOne({ serialNumber });
|
const cert = await certificateDAL.findOne({ serialNumber });
|
||||||
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
||||||
@@ -77,6 +83,11 @@ export const certificateServiceFactory = ({
|
|||||||
return deletedCert;
|
return deletedCert;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoke certificate with serial number [serialNumber].
|
||||||
|
* Note: Revoking a certificate adds it to the certificate revocation list (CRL)
|
||||||
|
* of its issuing CA
|
||||||
|
*/
|
||||||
const revokeCert = async ({
|
const revokeCert = async ({
|
||||||
serialNumber,
|
serialNumber,
|
||||||
revocationReason,
|
revocationReason,
|
||||||
@@ -126,10 +137,13 @@ export const certificateServiceFactory = ({
|
|||||||
return { revokedAt };
|
return { revokedAt };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return certificate body and certificate chain for certificate with
|
||||||
|
* serial number [serialNumber]
|
||||||
|
*/
|
||||||
const getCertCert = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TGetCertCertDTO) => {
|
const getCertCert = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TGetCertCertDTO) => {
|
||||||
const cert = await certificateDAL.findOne({ serialNumber });
|
const cert = await certificateDAL.findOne({ serialNumber });
|
||||||
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
||||||
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
@@ -154,25 +168,19 @@ export const certificateServiceFactory = ({
|
|||||||
cipherTextBlob: certCert.encryptedCertificate
|
cipherTextBlob: certCert.encryptedCertificate
|
||||||
});
|
});
|
||||||
|
|
||||||
const caCertChain = await kmsService.decrypt({
|
|
||||||
kmsId: keyId,
|
|
||||||
cipherTextBlob: caCert.encryptedCertificateChain
|
|
||||||
});
|
|
||||||
|
|
||||||
const certObj = new x509.X509Certificate(decryptedCert);
|
const certObj = new x509.X509Certificate(decryptedCert);
|
||||||
|
|
||||||
const decryptedCaCert = await kmsService.decrypt({
|
const { caCert, caCertChain } = await getCaCertChain({
|
||||||
kmsId: keyId,
|
caId: ca.id,
|
||||||
cipherTextBlob: caCert.encryptedCertificate
|
certificateAuthorityDAL,
|
||||||
|
certificateAuthorityCertDAL,
|
||||||
|
projectDAL,
|
||||||
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
const caCertObj = new x509.X509Certificate(decryptedCaCert);
|
|
||||||
|
|
||||||
const certificateChain = `${caCertObj.toString("pem")}\n${caCertChain.toString("utf-8")}`.trim();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
certificate: certObj.toString("pem"),
|
certificate: certObj.toString("pem"),
|
||||||
certificateChain,
|
certificateChain: `${caCert}\n${caCertChain}`.trim(),
|
||||||
serialNumber: certObj.serialNumber
|
serialNumber: certObj.serialNumber
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user