mirror of https://github.com/certd/certd
feat: 支持ECC类型
parent
d4092e4929
commit
a7424e02f5
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -342,7 +342,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
|||
title: "历史记录保持数",
|
||||
type: "number",
|
||||
form: {
|
||||
value: 10,
|
||||
value: 20,
|
||||
helper: "历史记录保持条数,多余的会被删除"
|
||||
},
|
||||
column: {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export const Constants = {
|
||||
dataDir: './data',
|
||||
role: {
|
||||
defaultUser: 3,
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
Loading…
Reference in New Issue