mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-13 22:29:33 +00:00
Merge pull request #2239 from Infisical/ca-renewal
CA Renewal (Same Key Pair)
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasTable(TableName.CertificateAuthority)) {
|
||||
const hasActiveCaCertIdColumn = await knex.schema.hasColumn(TableName.CertificateAuthority, "activeCaCertId");
|
||||
if (!hasActiveCaCertIdColumn) {
|
||||
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||
t.uuid("activeCaCertId").nullable();
|
||||
t.foreign("activeCaCertId").references("id").inTable(TableName.CertificateAuthorityCert);
|
||||
});
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE "${TableName.CertificateAuthority}" ca
|
||||
SET "activeCaCertId" = cac.id
|
||||
FROM "${TableName.CertificateAuthorityCert}" cac
|
||||
WHERE ca.id = cac."caId"
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
if (await knex.schema.hasTable(TableName.CertificateAuthorityCert)) {
|
||||
const hasVersionColumn = await knex.schema.hasColumn(TableName.CertificateAuthorityCert, "version");
|
||||
if (!hasVersionColumn) {
|
||||
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
|
||||
t.integer("version").nullable();
|
||||
t.dropUnique(["caId"]);
|
||||
});
|
||||
|
||||
await knex(TableName.CertificateAuthorityCert).update({ version: 1 }).whereNull("version");
|
||||
|
||||
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
|
||||
t.integer("version").notNullable().alter();
|
||||
});
|
||||
}
|
||||
|
||||
const hasCaSecretIdColumn = await knex.schema.hasColumn(TableName.CertificateAuthorityCert, "caSecretId");
|
||||
if (!hasCaSecretIdColumn) {
|
||||
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
|
||||
t.uuid("caSecretId").nullable();
|
||||
t.foreign("caSecretId").references("id").inTable(TableName.CertificateAuthoritySecret).onDelete("CASCADE");
|
||||
});
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE "${TableName.CertificateAuthorityCert}" cert
|
||||
SET "caSecretId" = (
|
||||
SELECT sec.id
|
||||
FROM "${TableName.CertificateAuthoritySecret}" sec
|
||||
WHERE sec."caId" = cert."caId"
|
||||
)
|
||||
`);
|
||||
|
||||
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
|
||||
t.uuid("caSecretId").notNullable().alter();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (await knex.schema.hasTable(TableName.CertificateAuthoritySecret)) {
|
||||
await knex.schema.alterTable(TableName.CertificateAuthoritySecret, (t) => {
|
||||
t.dropUnique(["caId"]);
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasTable(TableName.Certificate)) {
|
||||
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||
t.uuid("caCertId").nullable();
|
||||
t.foreign("caCertId").references("id").inTable(TableName.CertificateAuthorityCert);
|
||||
});
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE "${TableName.Certificate}" cert
|
||||
SET "caCertId" = (
|
||||
SELECT caCert.id
|
||||
FROM "${TableName.CertificateAuthorityCert}" caCert
|
||||
WHERE caCert."caId" = cert."caId"
|
||||
)
|
||||
`);
|
||||
|
||||
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||
t.uuid("caCertId").notNullable().alter();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasTable(TableName.CertificateAuthority)) {
|
||||
if (await knex.schema.hasColumn(TableName.CertificateAuthority, "activeCaCertId")) {
|
||||
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||
t.dropColumn("activeCaCertId");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (await knex.schema.hasTable(TableName.CertificateAuthorityCert)) {
|
||||
if (await knex.schema.hasColumn(TableName.CertificateAuthorityCert, "version")) {
|
||||
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
|
||||
t.dropColumn("version");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.CertificateAuthorityCert, "caSecretId")) {
|
||||
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
|
||||
t.dropColumn("caSecretId");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (await knex.schema.hasTable(TableName.Certificate)) {
|
||||
if (await knex.schema.hasColumn(TableName.Certificate, "caCertId")) {
|
||||
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||
t.dropColumn("caCertId");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,8 @@ export const CertificateAuthoritiesSchema = z.object({
|
||||
maxPathLength: z.number().nullable().optional(),
|
||||
keyAlgorithm: z.string(),
|
||||
notBefore: z.date().nullable().optional(),
|
||||
notAfter: z.date().nullable().optional()
|
||||
notAfter: z.date().nullable().optional(),
|
||||
activeCaCertId: z.string().uuid().nullable().optional()
|
||||
});
|
||||
|
||||
export type TCertificateAuthorities = z.infer<typeof CertificateAuthoritiesSchema>;
|
||||
|
||||
@@ -15,7 +15,9 @@ export const CertificateAuthorityCertsSchema = z.object({
|
||||
updatedAt: z.date(),
|
||||
caId: z.string().uuid(),
|
||||
encryptedCertificate: zodBuffer,
|
||||
encryptedCertificateChain: zodBuffer
|
||||
encryptedCertificateChain: zodBuffer,
|
||||
version: z.number(),
|
||||
caSecretId: z.string().uuid()
|
||||
});
|
||||
|
||||
export type TCertificateAuthorityCerts = z.infer<typeof CertificateAuthorityCertsSchema>;
|
||||
|
||||
@@ -20,7 +20,8 @@ export const CertificatesSchema = z.object({
|
||||
notAfter: z.date(),
|
||||
revokedAt: z.date().nullable().optional(),
|
||||
revocationReason: z.number().nullable().optional(),
|
||||
altNames: z.string().default("").nullable().optional()
|
||||
altNames: z.string().default("").nullable().optional(),
|
||||
caCertId: z.string().uuid()
|
||||
});
|
||||
|
||||
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
||||
|
||||
@@ -130,7 +130,9 @@ export enum EventType {
|
||||
GET_CA = "get-certificate-authority",
|
||||
UPDATE_CA = "update-certificate-authority",
|
||||
DELETE_CA = "delete-certificate-authority",
|
||||
RENEW_CA = "renew-certificate-authority",
|
||||
GET_CA_CSR = "get-certificate-authority-csr",
|
||||
GET_CA_CERTS = "get-certificate-authority-certs",
|
||||
GET_CA_CERT = "get-certificate-authority-cert",
|
||||
SIGN_INTERMEDIATE = "sign-intermediate",
|
||||
IMPORT_CA_CERT = "import-certificate-authority-cert",
|
||||
@@ -1096,6 +1098,14 @@ interface DeleteCa {
|
||||
};
|
||||
}
|
||||
|
||||
interface RenewCa {
|
||||
type: EventType.RENEW_CA;
|
||||
metadata: {
|
||||
caId: string;
|
||||
dn: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetCaCsr {
|
||||
type: EventType.GET_CA_CSR;
|
||||
metadata: {
|
||||
@@ -1104,6 +1114,14 @@ interface GetCaCsr {
|
||||
};
|
||||
}
|
||||
|
||||
interface GetCaCerts {
|
||||
type: EventType.GET_CA_CERTS;
|
||||
metadata: {
|
||||
caId: string;
|
||||
dn: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetCaCert {
|
||||
type: EventType.GET_CA_CERT;
|
||||
metadata: {
|
||||
@@ -1349,7 +1367,9 @@ export type Event =
|
||||
| GetCa
|
||||
| UpdateCa
|
||||
| DeleteCa
|
||||
| RenewCa
|
||||
| GetCaCsr
|
||||
| GetCaCerts
|
||||
| GetCaCert
|
||||
| SignIntermediate
|
||||
| ImportCaCert
|
||||
|
||||
@@ -1049,12 +1049,27 @@ export const CERTIFICATE_AUTHORITIES = {
|
||||
caId: "The ID of the CA to generate CSR from",
|
||||
csr: "The generated CSR from the CA"
|
||||
},
|
||||
RENEW_CA_CERT: {
|
||||
caId: "The ID of the CA to renew the CA certificate for",
|
||||
type: "The type of behavior to use for the renewal operation. Currently Infisical is only able to renew a CA certificate with the same key pair.",
|
||||
notAfter: "The expiry date and time for the renewed CA certificate in YYYY-MM-DDTHH:mm:ss.sssZ format",
|
||||
certificate: "The renewed CA certificate body",
|
||||
certificateChain: "The certificate chain of the CA",
|
||||
serialNumber: "The serial number of the renewed CA certificate"
|
||||
},
|
||||
GET_CERT: {
|
||||
caId: "The ID of the CA to get the certificate body and certificate chain from",
|
||||
certificate: "The certificate body of the CA",
|
||||
certificateChain: "The certificate chain of the CA",
|
||||
serialNumber: "The serial number of the CA certificate"
|
||||
},
|
||||
GET_CA_CERTS: {
|
||||
caId: "The ID of the CA to get the CA certificates for",
|
||||
certificate: "The certificate body of the CA certificate",
|
||||
certificateChain: "The certificate chain of the CA certificate",
|
||||
serialNumber: "The serial number of the CA certificate",
|
||||
version: "The version of the CA certificate. The version is incremented for each CA renewal operation."
|
||||
},
|
||||
SIGN_INTERMEDIATE: {
|
||||
caId: "The ID of the CA to sign the intermediate certificate with",
|
||||
csr: "The pem-encoded CSR to sign with the CA",
|
||||
|
||||
@@ -8,7 +8,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
||||
import { CaStatus, CaType } from "@app/services/certificate-authority/certificate-authority-types";
|
||||
import { CaRenewalType, CaStatus, CaType } from "@app/services/certificate-authority/certificate-authority-types";
|
||||
import {
|
||||
validateAltNamesField,
|
||||
validateCaDateField
|
||||
@@ -275,15 +275,118 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:caId/renew",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Perform CA certificate renewal",
|
||||
params: z.object({
|
||||
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.caId)
|
||||
}),
|
||||
body: z.object({
|
||||
type: z.nativeEnum(CaRenewalType).describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.type),
|
||||
notAfter: validateCaDateField.describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.notAfter)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
certificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.certificate),
|
||||
certificateChain: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.certificateChain),
|
||||
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.serialNumber)
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { certificate, certificateChain, serialNumber, ca } =
|
||||
await server.services.certificateAuthority.renewCaCert({
|
||||
caId: req.params.caId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: ca.projectId,
|
||||
event: {
|
||||
type: EventType.RENEW_CA,
|
||||
metadata: {
|
||||
caId: ca.id,
|
||||
dn: ca.dn
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
certificate,
|
||||
certificateChain,
|
||||
serialNumber
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:caId/certificate",
|
||||
url: "/:caId/ca-certificates",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Get cert and cert chain of a CA",
|
||||
description: "Get list of past and current CA certificates for a CA",
|
||||
params: z.object({
|
||||
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.caId)
|
||||
}),
|
||||
response: {
|
||||
200: z.array(
|
||||
z.object({
|
||||
certificate: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.certificate),
|
||||
certificateChain: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.certificateChain),
|
||||
serialNumber: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.serialNumber),
|
||||
version: z.number().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.version)
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { caCerts, ca } = await server.services.certificateAuthority.getCaCerts({
|
||||
caId: req.params.caId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: ca.projectId,
|
||||
event: {
|
||||
type: EventType.GET_CA_CERTS,
|
||||
metadata: {
|
||||
caId: ca.id,
|
||||
dn: ca.dn
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return caCerts;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:caId/certificate", // TODO: consider updating endpoint structure considering CA certificates
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Get current CA cert and cert chain of a CA",
|
||||
params: z.object({
|
||||
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CERT.caId)
|
||||
}),
|
||||
|
||||
@@ -5,7 +5,13 @@ import { BadRequestError } from "@app/lib/errors";
|
||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||
|
||||
import { CertKeyAlgorithm, CertStatus } from "../certificate/certificate-types";
|
||||
import { TDNParts, TGetCaCertChainDTO, TGetCaCredentialsDTO, TRebuildCaCrlDTO } from "./certificate-authority-types";
|
||||
import {
|
||||
TDNParts,
|
||||
TGetCaCertChainDTO,
|
||||
TGetCaCertChainsDTO,
|
||||
TGetCaCredentialsDTO,
|
||||
TRebuildCaCrlDTO
|
||||
} from "./certificate-authority-types";
|
||||
|
||||
export const createDistinguishedName = (parts: TDNParts) => {
|
||||
const dnParts = [];
|
||||
@@ -89,6 +95,8 @@ 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
|
||||
*
|
||||
* TODO: Update to get latest CA Secret once support for CA renewal with new key pair is added
|
||||
*/
|
||||
export const getCaCredentials = async ({
|
||||
caId,
|
||||
@@ -132,26 +140,73 @@ export const getCaCredentials = async ({
|
||||
]);
|
||||
|
||||
return {
|
||||
caSecret,
|
||||
caPrivateKey,
|
||||
caPublicKey
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the decrypted pem-encoded certificate and certificate chain
|
||||
* Return the list of decrypted pem-encoded certificates and certificate chains
|
||||
* for CA with id [caId].
|
||||
*/
|
||||
export const getCaCertChain = async ({
|
||||
export const getCaCertChains = async ({
|
||||
caId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
}: TGetCaCertChainDTO) => {
|
||||
}: TGetCaCertChainsDTO) => {
|
||||
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 kmsDecryptor = await kmsService.decryptWithKmsKey({
|
||||
kmsId: keyId
|
||||
});
|
||||
|
||||
const caCerts = await certificateAuthorityCertDAL.find({ caId: ca.id }, { sort: [["version", "asc"]] });
|
||||
|
||||
const decryptedChains = await Promise.all(
|
||||
caCerts.map(async (caCert) => {
|
||||
const decryptedCaCert = await kmsDecryptor({
|
||||
cipherTextBlob: caCert.encryptedCertificate
|
||||
});
|
||||
const caCertObj = new x509.X509Certificate(decryptedCaCert);
|
||||
const decryptedChain = await kmsDecryptor({
|
||||
cipherTextBlob: caCert.encryptedCertificateChain
|
||||
});
|
||||
return {
|
||||
certificate: caCertObj.toString("pem"),
|
||||
certificateChain: decryptedChain.toString("utf-8"),
|
||||
serialNumber: caCertObj.serialNumber,
|
||||
version: caCert.version
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return decryptedChains;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the decrypted pem-encoded certificate and certificate chain
|
||||
* corresponding to CA certificate with id [caCertId].
|
||||
*/
|
||||
export const getCaCertChain = async ({
|
||||
caCertId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
}: TGetCaCertChainDTO) => {
|
||||
const caCert = await certificateAuthorityCertDAL.findById(caCertId);
|
||||
if (!caCert) throw new BadRequestError({ message: "CA certificate not found" });
|
||||
const ca = await certificateAuthorityDAL.findById(caCert.caId);
|
||||
|
||||
const keyId = await getProjectKmsCertificateKeyId({
|
||||
projectId: ca.projectId,
|
||||
|
||||
@@ -20,7 +20,8 @@ import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cer
|
||||
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
||||
import {
|
||||
createDistinguishedName,
|
||||
getCaCertChain,
|
||||
getCaCertChain, // TODO: consider rename
|
||||
getCaCertChains,
|
||||
getCaCredentials,
|
||||
keyAlgorithmToAlgCfg,
|
||||
parseDistinguishedName
|
||||
@@ -33,10 +34,12 @@ import {
|
||||
TCreateCaDTO,
|
||||
TDeleteCaDTO,
|
||||
TGetCaCertDTO,
|
||||
TGetCaCertsDTO,
|
||||
TGetCaCsrDTO,
|
||||
TGetCaDTO,
|
||||
TImportCertToCaDTO,
|
||||
TIssueCertFromCaDTO,
|
||||
TRenewCaCertDTO,
|
||||
TSignCertFromCaDTO,
|
||||
TSignIntermediateDTO,
|
||||
TUpdateCaDTO
|
||||
@@ -48,7 +51,10 @@ type TCertificateAuthorityServiceFactoryDep = {
|
||||
TCertificateAuthorityDALFactory,
|
||||
"transaction" | "create" | "findById" | "updateById" | "deleteById" | "findOne"
|
||||
>;
|
||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "create" | "findOne" | "transaction">;
|
||||
certificateAuthorityCertDAL: Pick<
|
||||
TCertificateAuthorityCertDALFactory,
|
||||
"create" | "findOne" | "transaction" | "find" | "findById"
|
||||
>;
|
||||
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "create" | "findOne">;
|
||||
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "create" | "findOne" | "update">;
|
||||
certificateAuthorityQueue: TCertificateAuthorityQueueFactory; // TODO: Pick
|
||||
@@ -165,6 +171,24 @@ export const certificateAuthorityServiceFactory = ({
|
||||
kmsId: certificateManagerKmsId
|
||||
});
|
||||
|
||||
// https://nodejs.org/api/crypto.html#static-method-keyobjectfromkey
|
||||
const skObj = KeyObject.from(keys.privateKey);
|
||||
|
||||
const { cipherTextBlob: encryptedPrivateKey } = await kmsEncryptor({
|
||||
plainText: skObj.export({
|
||||
type: "pkcs8",
|
||||
format: "der"
|
||||
})
|
||||
});
|
||||
|
||||
const caSecret = await certificateAuthoritySecretDAL.create(
|
||||
{
|
||||
caId: ca.id,
|
||||
encryptedPrivateKey
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
if (type === CaType.ROOT) {
|
||||
// note: create self-signed cert only applicable for root CA
|
||||
const cert = await x509.X509CertificateGenerator.createSelfSigned({
|
||||
@@ -191,11 +215,21 @@ export const certificateAuthorityServiceFactory = ({
|
||||
plainText: Buffer.alloc(0)
|
||||
});
|
||||
|
||||
await certificateAuthorityCertDAL.create(
|
||||
const caCert = await certificateAuthorityCertDAL.create(
|
||||
{
|
||||
caId: ca.id,
|
||||
encryptedCertificate,
|
||||
encryptedCertificateChain
|
||||
encryptedCertificateChain,
|
||||
version: 1,
|
||||
caSecretId: caSecret.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
await certificateAuthorityDAL.updateById(
|
||||
ca.id,
|
||||
{
|
||||
activeCaCertId: caCert.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -223,24 +257,6 @@ export const certificateAuthorityServiceFactory = ({
|
||||
tx
|
||||
);
|
||||
|
||||
// https://nodejs.org/api/crypto.html#static-method-keyobjectfromkey
|
||||
const skObj = KeyObject.from(keys.privateKey);
|
||||
|
||||
const { cipherTextBlob: encryptedPrivateKey } = await kmsEncryptor({
|
||||
plainText: skObj.export({
|
||||
type: "pkcs8",
|
||||
format: "der"
|
||||
})
|
||||
});
|
||||
|
||||
await certificateAuthoritySecretDAL.create(
|
||||
{
|
||||
caId: ca.id,
|
||||
encryptedPrivateKey
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
return ca;
|
||||
});
|
||||
|
||||
@@ -341,9 +357,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
);
|
||||
|
||||
if (ca.type === CaType.ROOT) throw new BadRequestError({ message: "Root CA cannot generate CSR" });
|
||||
|
||||
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
||||
if (caCert) throw new BadRequestError({ message: "CA already has a certificate installed" });
|
||||
if (ca.activeCaCertId) throw new BadRequestError({ message: "CA already has a certificate installed" });
|
||||
|
||||
const { caPrivateKey, caPublicKey } = await getCaCredentials({
|
||||
caId,
|
||||
@@ -381,9 +395,283 @@ export const certificateAuthorityServiceFactory = ({
|
||||
};
|
||||
|
||||
/**
|
||||
* Return certificate and certificate chain for CA
|
||||
* Renew certificate for CA with id [caId]
|
||||
* Note: Currently implements CA renewal with same key-pair only
|
||||
*/
|
||||
const getCaCert = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaCertDTO) => {
|
||||
const renewCaCert = async ({ caId, notAfter, actorId, actorAuthMethod, actor, actorOrgId }: TRenewCaCertDTO) => {
|
||||
const ca = await certificateAuthorityDAL.findById(caId);
|
||||
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||
|
||||
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
ca.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
);
|
||||
|
||||
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
||||
|
||||
// get latest CA certificate
|
||||
const caCert = await certificateAuthorityCertDAL.findById(ca.activeCaCertId);
|
||||
|
||||
const serialNumber = crypto.randomBytes(32).toString("hex");
|
||||
|
||||
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
|
||||
projectId: ca.projectId,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const kmsEncryptor = await kmsService.encryptWithKmsKey({
|
||||
kmsId: certificateManagerKmsId
|
||||
});
|
||||
|
||||
const { caPrivateKey, caPublicKey, caSecret } = await getCaCredentials({
|
||||
caId: ca.id,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthoritySecretDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm);
|
||||
|
||||
const kmsDecryptor = await kmsService.decryptWithKmsKey({
|
||||
kmsId: certificateManagerKmsId
|
||||
});
|
||||
const decryptedCaCert = await kmsDecryptor({
|
||||
cipherTextBlob: caCert.encryptedCertificate
|
||||
});
|
||||
|
||||
const caCertObj = new x509.X509Certificate(decryptedCaCert);
|
||||
|
||||
let certificate = "";
|
||||
let certificateChain = "";
|
||||
|
||||
switch (ca.type) {
|
||||
case CaType.ROOT: {
|
||||
if (new Date(notAfter) <= new Date(caCertObj.notAfter)) {
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"New Root CA certificate must have notAfter date that is greater than the current certificate notAfter date"
|
||||
});
|
||||
}
|
||||
|
||||
const notBeforeDate = new Date();
|
||||
const cert = await x509.X509CertificateGenerator.createSelfSigned({
|
||||
name: ca.dn,
|
||||
serialNumber,
|
||||
notBefore: notBeforeDate,
|
||||
notAfter: new Date(notAfter),
|
||||
signingAlgorithm: alg,
|
||||
keys: {
|
||||
privateKey: caPrivateKey,
|
||||
publicKey: caPublicKey
|
||||
},
|
||||
extensions: [
|
||||
new x509.BasicConstraintsExtension(
|
||||
true,
|
||||
ca.maxPathLength === -1 || !ca.maxPathLength ? undefined : ca.maxPathLength,
|
||||
true
|
||||
),
|
||||
new x509.ExtendedKeyUsageExtension(["1.2.3.4.5.6.7", "2.3.4.5.6.7.8"], true),
|
||||
// eslint-disable-next-line no-bitwise
|
||||
new x509.KeyUsagesExtension(x509.KeyUsageFlags.keyCertSign | x509.KeyUsageFlags.cRLSign, true),
|
||||
await x509.SubjectKeyIdentifierExtension.create(caPublicKey)
|
||||
]
|
||||
});
|
||||
|
||||
const { cipherTextBlob: encryptedCertificate } = await kmsEncryptor({
|
||||
plainText: Buffer.from(new Uint8Array(cert.rawData))
|
||||
});
|
||||
|
||||
const { cipherTextBlob: encryptedCertificateChain } = await kmsEncryptor({
|
||||
plainText: Buffer.alloc(0)
|
||||
});
|
||||
|
||||
await certificateAuthorityDAL.transaction(async (tx) => {
|
||||
const newCaCert = await certificateAuthorityCertDAL.create(
|
||||
{
|
||||
caId: ca.id,
|
||||
encryptedCertificate,
|
||||
encryptedCertificateChain,
|
||||
version: caCert.version + 1,
|
||||
caSecretId: caSecret.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
await certificateAuthorityDAL.updateById(
|
||||
ca.id,
|
||||
{
|
||||
activeCaCertId: newCaCert.id,
|
||||
notBefore: notBeforeDate,
|
||||
notAfter: new Date(notAfter)
|
||||
},
|
||||
tx
|
||||
);
|
||||
});
|
||||
|
||||
certificate = cert.toString("pem");
|
||||
break;
|
||||
}
|
||||
case CaType.INTERMEDIATE: {
|
||||
if (!ca.parentCaId) {
|
||||
// TODO: look into optimal way to support renewal of intermediate CA with external parent CA
|
||||
throw new BadRequestError({
|
||||
message: "Failed to renew intermediate CA certificate with external parent CA"
|
||||
});
|
||||
}
|
||||
|
||||
const parentCa = await certificateAuthorityDAL.findById(ca.parentCaId);
|
||||
const { caPrivateKey: parentCaPrivateKey } = await getCaCredentials({
|
||||
caId: parentCa.id,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthoritySecretDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
// get latest parent CA certificate
|
||||
if (!parentCa.activeCaCertId)
|
||||
throw new BadRequestError({ message: "Parent CA does not have a certificate installed" });
|
||||
const parentCaCert = await certificateAuthorityCertDAL.findById(parentCa.activeCaCertId);
|
||||
|
||||
const decryptedParentCaCert = await kmsDecryptor({
|
||||
cipherTextBlob: parentCaCert.encryptedCertificate
|
||||
});
|
||||
|
||||
const parentCaCertObj = new x509.X509Certificate(decryptedParentCaCert);
|
||||
|
||||
if (new Date(notAfter) <= new Date(caCertObj.notAfter)) {
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"New Intermediate CA certificate must have notAfter date that is greater than the current certificate notAfter date"
|
||||
});
|
||||
}
|
||||
|
||||
if (new Date(notAfter) > new Date(parentCaCertObj.notAfter)) {
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"New Intermediate CA certificate must have notAfter date that is equal to or smaller than the notAfter date of the parent CA certificate current certificate notAfter date"
|
||||
});
|
||||
}
|
||||
|
||||
const csrObj = await x509.Pkcs10CertificateRequestGenerator.create({
|
||||
name: ca.dn,
|
||||
keys: {
|
||||
privateKey: caPrivateKey,
|
||||
publicKey: caPublicKey
|
||||
},
|
||||
signingAlgorithm: alg,
|
||||
extensions: [
|
||||
// eslint-disable-next-line no-bitwise
|
||||
new x509.KeyUsagesExtension(
|
||||
x509.KeyUsageFlags.keyCertSign |
|
||||
x509.KeyUsageFlags.cRLSign |
|
||||
x509.KeyUsageFlags.digitalSignature |
|
||||
x509.KeyUsageFlags.keyEncipherment
|
||||
)
|
||||
],
|
||||
attributes: [new x509.ChallengePasswordAttribute("password")]
|
||||
});
|
||||
|
||||
const notBeforeDate = new Date();
|
||||
const intermediateCert = await x509.X509CertificateGenerator.create({
|
||||
serialNumber,
|
||||
subject: csrObj.subject,
|
||||
issuer: parentCaCertObj.subject,
|
||||
notBefore: notBeforeDate,
|
||||
notAfter: new Date(notAfter),
|
||||
signingKey: parentCaPrivateKey,
|
||||
publicKey: csrObj.publicKey,
|
||||
signingAlgorithm: alg,
|
||||
extensions: [
|
||||
new x509.KeyUsagesExtension(
|
||||
x509.KeyUsageFlags.keyCertSign |
|
||||
x509.KeyUsageFlags.cRLSign |
|
||||
x509.KeyUsageFlags.digitalSignature |
|
||||
x509.KeyUsageFlags.keyEncipherment,
|
||||
true
|
||||
),
|
||||
new x509.BasicConstraintsExtension(
|
||||
true,
|
||||
ca.maxPathLength === -1 || !ca.maxPathLength ? undefined : ca.maxPathLength,
|
||||
true
|
||||
),
|
||||
await x509.AuthorityKeyIdentifierExtension.create(parentCaCertObj, false),
|
||||
await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey)
|
||||
]
|
||||
});
|
||||
|
||||
const { cipherTextBlob: encryptedCertificate } = await kmsEncryptor({
|
||||
plainText: Buffer.from(new Uint8Array(intermediateCert.rawData))
|
||||
});
|
||||
|
||||
const { caCert: parentCaCertificate, caCertChain: parentCaCertChain } = await getCaCertChain({
|
||||
caCertId: parentCa.activeCaCertId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
certificateChain = `${parentCaCertificate}\n${parentCaCertChain}`.trim();
|
||||
|
||||
const { cipherTextBlob: encryptedCertificateChain } = await kmsEncryptor({
|
||||
plainText: Buffer.from(certificateChain)
|
||||
});
|
||||
|
||||
await certificateAuthorityDAL.transaction(async (tx) => {
|
||||
const newCaCert = await certificateAuthorityCertDAL.create(
|
||||
{
|
||||
caId: ca.id,
|
||||
encryptedCertificate,
|
||||
encryptedCertificateChain,
|
||||
version: caCert.version + 1,
|
||||
caSecretId: caSecret.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
await certificateAuthorityDAL.updateById(
|
||||
ca.id,
|
||||
{
|
||||
activeCaCertId: newCaCert.id,
|
||||
notBefore: notBeforeDate,
|
||||
notAfter: new Date(notAfter)
|
||||
},
|
||||
tx
|
||||
);
|
||||
});
|
||||
|
||||
certificate = intermediateCert.toString("pem");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new BadRequestError({
|
||||
message: "Unrecognized CA type"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
certificate,
|
||||
certificateChain,
|
||||
serialNumber,
|
||||
ca
|
||||
};
|
||||
};
|
||||
|
||||
const getCaCerts = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaCertsDTO) => {
|
||||
const ca = await certificateAuthorityDAL.findById(caId);
|
||||
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||
|
||||
@@ -400,7 +688,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
);
|
||||
|
||||
const { caCert, caCertChain, serialNumber } = await getCaCertChain({
|
||||
const caCertChains = await getCaCertChains({
|
||||
caId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
@@ -408,6 +696,41 @@ export const certificateAuthorityServiceFactory = ({
|
||||
kmsService
|
||||
});
|
||||
|
||||
return {
|
||||
ca,
|
||||
caCerts: caCertChains
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Return current certificate and certificate chain for CA
|
||||
*/
|
||||
const getCaCert = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaCertDTO) => {
|
||||
const ca = await certificateAuthorityDAL.findById(caId);
|
||||
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
ca.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
);
|
||||
|
||||
const { caCert, caCertChain, serialNumber } = await getCaCertChain({
|
||||
caCertId: ca.activeCaCertId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
return {
|
||||
certificate: caCert,
|
||||
certificateChain: caCertChain,
|
||||
@@ -447,6 +770,13 @@ export const certificateAuthorityServiceFactory = ({
|
||||
);
|
||||
|
||||
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
||||
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||
|
||||
const caCert = await certificateAuthorityCertDAL.findById(ca.activeCaCertId);
|
||||
|
||||
if (ca.notAfter && new Date() > new Date(ca.notAfter)) {
|
||||
throw new BadRequestError({ message: "CA is expired" });
|
||||
}
|
||||
|
||||
const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm);
|
||||
|
||||
@@ -459,7 +789,6 @@ export const certificateAuthorityServiceFactory = ({
|
||||
kmsId: certificateManagerKmsId
|
||||
});
|
||||
|
||||
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
||||
const decryptedCaCert = await kmsDecryptor({
|
||||
cipherTextBlob: caCert.encryptedCertificate
|
||||
});
|
||||
@@ -531,7 +860,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||
caId,
|
||||
caCertId: ca.activeCaCertId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
@@ -577,8 +906,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
);
|
||||
|
||||
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
||||
if (caCert) throw new BadRequestError({ message: "CA has already imported a certificate" });
|
||||
if (ca.activeCaCertId) throw new BadRequestError({ message: "CA has already imported a certificate" });
|
||||
|
||||
const certObj = new x509.X509Certificate(certificate);
|
||||
const maxPathLength = certObj.getExtension(x509.BasicConstraintsExtension)?.pathLength;
|
||||
@@ -625,12 +953,32 @@ export const certificateAuthorityServiceFactory = ({
|
||||
plainText: Buffer.from(certificateChain)
|
||||
});
|
||||
|
||||
// TODO: validate that latest key-pair of CA is used to sign the certificate
|
||||
// once renewal with new key pair is supported
|
||||
const { caSecret, caPublicKey } = await getCaCredentials({
|
||||
caId: ca.id,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthoritySecretDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const isCaAndCertPublicKeySame = Buffer.from(await crypto.subtle.exportKey("spki", caPublicKey)).equals(
|
||||
Buffer.from(certObj.publicKey.rawData)
|
||||
);
|
||||
|
||||
if (!isCaAndCertPublicKeySame) {
|
||||
throw new BadRequestError({ message: "CA and certificate public key do not match" });
|
||||
}
|
||||
|
||||
await certificateAuthorityCertDAL.transaction(async (tx) => {
|
||||
await certificateAuthorityCertDAL.create(
|
||||
const newCaCert = await certificateAuthorityCertDAL.create(
|
||||
{
|
||||
caId: ca.id,
|
||||
encryptedCertificate,
|
||||
encryptedCertificateChain
|
||||
encryptedCertificateChain,
|
||||
version: 1,
|
||||
caSecretId: caSecret.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -643,7 +991,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
notBefore: new Date(certObj.notBefore),
|
||||
notAfter: new Date(certObj.notAfter),
|
||||
serialNumber: certObj.serialNumber,
|
||||
parentCaId: parentCa?.id
|
||||
parentCaId: parentCa?.id,
|
||||
activeCaCertId: newCaCert.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -683,9 +1032,12 @@ export const certificateAuthorityServiceFactory = ({
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates);
|
||||
|
||||
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
||||
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||
const caCert = await certificateAuthorityCertDAL.findById(ca.activeCaCertId);
|
||||
|
||||
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
||||
if (!caCert) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||
if (ca.notAfter && new Date() > new Date(ca.notAfter)) {
|
||||
throw new BadRequestError({ message: "CA is expired" });
|
||||
}
|
||||
|
||||
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
|
||||
projectId: ca.projectId,
|
||||
@@ -814,6 +1166,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
const cert = await certificateDAL.create(
|
||||
{
|
||||
caId: ca.id,
|
||||
caCertId: caCert.id,
|
||||
status: CertStatus.ACTIVE,
|
||||
friendlyName: friendlyName || commonName,
|
||||
commonName,
|
||||
@@ -837,7 +1190,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||
caId: ca.id,
|
||||
caCertId: caCert.id,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
@@ -886,9 +1239,13 @@ export const certificateAuthorityServiceFactory = ({
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates);
|
||||
|
||||
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
||||
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||
|
||||
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
||||
if (!caCert) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||
const caCert = await certificateAuthorityCertDAL.findById(ca.activeCaCertId);
|
||||
|
||||
if (ca.notAfter && new Date() > new Date(ca.notAfter)) {
|
||||
throw new BadRequestError({ message: "CA is expired" });
|
||||
}
|
||||
|
||||
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
|
||||
projectId: ca.projectId,
|
||||
@@ -1013,6 +1370,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
const cert = await certificateDAL.create(
|
||||
{
|
||||
caId: ca.id,
|
||||
caCertId: caCert.id,
|
||||
status: CertStatus.ACTIVE,
|
||||
friendlyName: friendlyName || csrObj.subject,
|
||||
commonName: cn,
|
||||
@@ -1036,7 +1394,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||
caId: ca.id,
|
||||
caCertId: ca.activeCaCertId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
@@ -1058,6 +1416,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
updateCaById,
|
||||
deleteCaById,
|
||||
getCaCsr,
|
||||
renewCaCert,
|
||||
getCaCerts,
|
||||
getCaCert,
|
||||
signIntermediate,
|
||||
importCertToCa,
|
||||
|
||||
@@ -20,6 +20,10 @@ export enum CaStatus {
|
||||
PENDING_CERTIFICATE = "pending-certificate"
|
||||
}
|
||||
|
||||
export enum CaRenewalType {
|
||||
EXISTING = "existing"
|
||||
}
|
||||
|
||||
export type TCreateCaDTO = {
|
||||
projectSlug: string;
|
||||
type: CaType;
|
||||
@@ -53,6 +57,16 @@ export type TGetCaCsrDTO = {
|
||||
caId: string;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TRenewCaCertDTO = {
|
||||
caId: string;
|
||||
notAfter: string;
|
||||
type: CaRenewalType;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TGetCaCertsDTO = {
|
||||
caId: string;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TGetCaCertDTO = {
|
||||
caId: string;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
@@ -109,10 +123,18 @@ export type TGetCaCredentialsDTO = {
|
||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||
};
|
||||
|
||||
export type TGetCaCertChainDTO = {
|
||||
export type TGetCaCertChainsDTO = {
|
||||
caId: string;
|
||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findOne">;
|
||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "find">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||
};
|
||||
|
||||
export type TGetCaCertChainDTO = {
|
||||
caCertId: string;
|
||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ type TCertificateServiceFactoryDep = {
|
||||
certificateDAL: Pick<TCertificateDALFactory, "findOne" | "deleteById" | "update" | "find">;
|
||||
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "findOne">;
|
||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findOne">;
|
||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">;
|
||||
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "update">;
|
||||
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "findById" | "transaction">;
|
||||
@@ -180,7 +180,7 @@ export const certificateServiceFactory = ({
|
||||
const certObj = new x509.X509Certificate(decryptedCert);
|
||||
|
||||
const { caCert, caCertChain } = await getCaCertChain({
|
||||
caId: ca.id,
|
||||
caCertId: cert.caCertId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
|
||||
Reference in New Issue
Block a user