feat: 支持ECC类型

pull/148/head
xiaojunnuo 2024-08-25 11:56:15 +08:00
parent d4092e4929
commit a7424e02f5
8 changed files with 124 additions and 16 deletions

View File

@ -14,7 +14,7 @@ export type CertInfo = {
csr: string;
};
export type SSLProvider = "letsencrypt" | "google" | "zerossl";
export type PrivateKeyType = "rsa" | "ec";
export type PrivateKeyType = "rsa_1024" | "rsa_2048" | "rsa_3072" | "rsa_4096" | "ec_256" | "ec_384" | "ec_521";
type AcmeServiceOptions = {
userContext: IContext;
logger: Logger;
@ -226,12 +226,16 @@ export class AcmeService {
/* Create CSR */
const { commonName, altNames } = this.buildCommonNameByDomains(domains);
let privateKey = null;
if (options.privateKeyType == "ec") {
privateKey = await acme.crypto.createPrivateEcdsaKey();
const privateKeyArr = options.privateKeyType.split("_");
const type = privateKeyArr[0];
const size = parseInt(privateKeyArr[1]);
if (type == "ec") {
const name: any = "P-" + size;
privateKey = await acme.crypto.createPrivateEcdsaKey(name);
} else {
privateKey = await acme.crypto.createPrivateRsaKey();
privateKey = await acme.crypto.createPrivateRsaKey(size);
}
const [key, csr] = await acme.forge.createCsr(
const [key, csr] = await acme.crypto.createCsr(
{
commonName,
...csrInfo,

View File

@ -133,10 +133,10 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
const cert: CertInfo = certReader.toCertInfo();
this.cert = cert;
this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.validity.notAfter).valueOf();
this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.notAfter).valueOf();
if (isNew) {
const applyTime = dayjs(certReader.detail.validity.notBefore).format("YYYYMMDD_HHmmss");
const applyTime = dayjs(certReader.detail.notBefore).format("YYYYMMDD_HHmmss");
await this.zipCert(cert, applyTime);
} else {
this.extendsFiles();

View File

@ -1,8 +1,8 @@
import { CertInfo } from "./acme.js";
import fs from "fs";
import os from "os";
import forge from "node-forge";
import path from "path";
import { crypto } from "@certd/acme-client";
export class CertReader implements CertInfo {
crt: string;
key: string;
@ -29,9 +29,8 @@ export class CertReader implements CertInfo {
}
getCrtDetail(crt: string) {
const pki = forge.pki;
const detail = pki.certificateFromPem(crt.toString());
const expires = detail.validity.notAfter;
const detail = crypto.readCertificateInfo(crt.toString());
const expires = detail.notAfter;
return { detail, expires };
}

View File

@ -44,13 +44,18 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
@TaskInput({
title: "证书私钥类型",
value: "rsa",
value: "rsa_2048",
component: {
name: "a-select",
vModel: "value",
options: [
{ value: "rsa", label: "RSA" },
{ value: "ec", label: "EC" },
{ value: "rsa_1024", label: "RSA 1024" },
{ value: "rsa_2048", label: "RSA 2048" },
{ value: "rsa_3072", label: "RSA 3072" },
{ value: "rsa_4096", label: "RSA 4096" },
{ value: "ec_256", label: "EC 256" },
{ value: "ec_384", label: "EC 384" },
{ value: "ec_521", label: "EC 521" },
],
},
required: true,

View File

@ -342,7 +342,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
title: "历史记录保持数",
type: "number",
form: {
value: 10,
value: 20,
helper: "历史记录保持条数,多余的会被删除"
},
column: {

View File

@ -1,4 +1,5 @@
export const Constants = {
dataDir: './data',
role: {
defaultUser: 3,
},

View File

@ -63,7 +63,7 @@ export class HistoryService extends BaseService<HistoryEntity> {
return id;
}
private async clear(pipelineId: number, keepCount = 10) {
private async clear(pipelineId: number, keepCount = 20) {
const count = await this.repository.count({
where: {
pipelineId,

View File

@ -0,0 +1,99 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
import { CertInfo, CertReader } from '@certd/plugin-cert';
import * as fs from 'fs';
import { Constants } from '../../../../basic/constants.js';
import path from 'path';
@IsTaskPlugin({
name: 'CopyToLocal',
title: '复制到本机',
group: pluginGroups.host.key,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
@TaskInput({
title: '证书保存路径',
helper: '需要有写入权限,路径要包含证书文件名,文件名不能用*?!等特殊符号\n推荐使用相对路径将写入与数据库同级目录无需映射例如./tmp/cert.pem',
component: {
placeholder: './tmp/cert.pem',
},
})
crtPath!: string;
@TaskInput({
title: '私钥保存路径',
helper: '需要有写入权限,路径要包含私钥文件名,文件名不能用*?!等特殊符号\n推荐使用相对路径将写入与数据库同级目录无需映射例如./tmp/cert.key',
component: {
placeholder: './tmp/cert.key',
},
})
keyPath!: string;
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'pi-output-selector',
},
required: true,
})
cert!: CertInfo;
@TaskOutput({
title: '证书保存路径',
})
hostCrtPath!: string;
@TaskOutput({
title: '私钥保存路径',
})
hostKeyPath!: string;
async onInstance() {}
copyFile(srcFile: string, destFile: string) {
this.logger.info(`复制文件:${srcFile} => ${destFile}`);
const dir = path.dirname(destFile);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.copyFileSync(srcFile, destFile);
}
async execute(): Promise<void> {
let { crtPath, keyPath } = this;
const certReader = new CertReader(this.cert);
this.logger.info('将证书写入本地缓存文件');
const saveCrtPath = certReader.saveToFile('crt');
const saveKeyPath = certReader.saveToFile('key');
this.logger.info('本地文件写入成功');
try {
this.logger.info('复制到目标路径');
crtPath = crtPath.startsWith('/') ? crtPath : path.join(Constants.dataDir, crtPath);
keyPath = keyPath.startsWith('/') ? keyPath : path.join(Constants.dataDir, keyPath);
// crtPath = path.resolve(crtPath);
// keyPath = path.resolve(keyPath);
this.copyFile(saveCrtPath, crtPath);
this.copyFile(saveKeyPath, keyPath);
this.logger.info('证书复制成功crtPath=', crtPath, ',keyPath=', keyPath);
this.logger.info('请注意,如果使用的是相对路径,那么文件就在你的数据库同级目录下,默认是/data/certd/下面');
this.logger.info('请注意,如果使用的是绝对路径,文件在容器内的目录下,你需要给容器做目录映射才能复制到宿主机');
} catch (e) {
this.logger.error(`复制失败:${e.message}`);
throw e;
} finally {
//删除临时文件
this.logger.info('删除临时文件');
fs.unlinkSync(saveCrtPath);
fs.unlinkSync(saveKeyPath);
}
this.logger.info('执行完成');
//输出
this.hostCrtPath = crtPath;
this.hostKeyPath = keyPath;
}
}
new CopyCertToLocalPlugin();