perf: 支持pfx、der

This commit is contained in:
xiaojunnuo
2024-09-05 15:36:35 +08:00
parent ecad7f58c1
commit fbeaed2035
7 changed files with 169 additions and 26 deletions

View File

@@ -0,0 +1,115 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, sp, TaskInput, TaskOutput } from "@certd/pipeline";
import type { CertInfo } from "../cert-plugin/acme.js";
import { CertReader, CertReaderHandleContext } from "../cert-plugin/cert-reader.js";
import path from "path";
import os from "os";
import fs from "fs";
export { CertReader };
export type { CertInfo };
@IsTaskPlugin({
name: "CertConvert",
title: "证书转换器",
group: pluginGroups.cert.key,
desc: "转换为pfx、der等证书格式",
default: {
input: {
renewDays: 20,
forceUpdate: false,
},
strategy: {
runStrategy: RunStrategy.AlwaysRun,
},
},
})
export class CertConvertPlugin extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "pi-output-selector",
from: "CertApply",
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: "PFX证书密码",
helper: "不填则没有密码",
component: {
name: "a-input-password",
vModel: "value",
},
required: true,
})
pfxPassword!: string;
@TaskOutput({
title: "pfx格式证书",
type: "PfxCert",
})
pfxCert?: string;
@TaskOutput({
title: "der格式证书",
type: "DerCert",
})
derCert?: string;
async onInit() {}
async execute(): Promise<void> {
const certReader = new CertReader(this.cert);
const handle = async (opts: CertReaderHandleContext) => {
// 调用openssl 转pfx
await this.convertPfx(opts);
// 转der
await this.convertDer(opts);
};
await certReader.readCertFile({ logger: this.logger, handle });
}
async exec(cmd: string) {
await sp.spawn({
cmd: cmd,
logger: this.logger,
});
}
private async convertPfx(opts: CertReaderHandleContext) {
const { reader, tmpCrtPath, tmpKeyPath } = opts;
const pfxPath = path.join(os.tmpdir(), "/certd/tmp/", Math.floor(Math.random() * 1000000) + "", "cert.pfx");
let passwordArg = "-passout pass:";
if (this.pfxPassword) {
passwordArg = `-password pass:${this.pfxPassword}`;
}
await this.exec(`openssl pkcs12 -export -out ${pfxPath} -inkey ${tmpKeyPath} -in ${tmpCrtPath} ${passwordArg}`);
this.pfxCert = pfxPath;
const applyTime = new Date().getTime();
const filename = reader.buildCertFileName("pfx", applyTime);
const fileBuffer = fs.readFileSync(pfxPath);
this.saveFile(filename, fileBuffer);
}
private async convertDer(opts: CertReaderHandleContext) {
const { reader, tmpCrtPath } = opts;
const derPath = path.join(os.tmpdir(), "/certd/tmp/", Math.floor(Math.random() * 1000000) + "", `cert.der`);
await this.exec(`openssl x509 -outform der -in ${tmpCrtPath} -out ${derPath}`);
this.derCert = derPath;
const applyTime = new Date().getTime();
const filename = reader.buildCertFileName("der", applyTime);
const fileBuffer = fs.readFileSync(derPath);
this.saveFile(filename, fileBuffer);
}
}
new CertConvertPlugin();

View File

@@ -3,6 +3,11 @@ import fs from "fs";
import os from "os";
import path from "path";
import { crypto } from "@certd/acme-client";
import { ILogger } from "@certd/pipeline";
export type CertReaderHandleContext = { reader: CertReader; tmpCrtPath: string; tmpKeyPath: string };
export type CertReaderHandle = (ctx: CertReaderHandleContext) => Promise<void>;
export type HandleOpts = { logger: ILogger; handle: CertReaderHandle };
export class CertReader implements CertInfo {
crt: string;
key: string;
@@ -28,7 +33,7 @@ export class CertReader implements CertInfo {
};
}
getCrtDetail(crt: string) {
getCrtDetail(crt: string = this.crt) {
const detail = crypto.readCertificateInfo(crt.toString());
const expires = detail.notAfter;
return { detail, expires };
@@ -48,4 +53,31 @@ export class CertReader implements CertInfo {
fs.writeFileSync(filepath, this[type]);
return filepath;
}
async readCertFile(opts: HandleOpts) {
const logger = opts.logger;
logger.info("将证书写入本地缓存文件");
const tmpCrtPath = this.saveToFile("crt");
const tmpKeyPath = this.saveToFile("key");
logger.info("本地文件写入成功");
try {
await opts.handle({
reader: this,
tmpCrtPath: tmpCrtPath,
tmpKeyPath: tmpKeyPath,
});
} finally {
//删除临时文件
logger.info("删除临时文件");
fs.unlinkSync(tmpCrtPath);
fs.unlinkSync(tmpKeyPath);
}
}
buildCertFileName(suffix: string, applyTime: number, prefix = "cert") {
const detail = this.getCrtDetail();
let domain = detail.detail.domains.commonName;
domain = domain.replace(".", "_").replace("*", "_");
return `${prefix}_${domain}_${applyTime}.${suffix}`;
}
}

View File

@@ -6,8 +6,8 @@ import { DnsProviderContext, DnsProviderDefine, dnsProviderRegistry } from "../.
import { CertReader } from "./cert-reader.js";
import { CertApplyBasePlugin } from "./base.js";
export { CertReader };
export type { CertInfo };
export * from "./cert-reader.js";
@IsTaskPlugin({
name: "CertApply",

View File

@@ -1,2 +1,3 @@
export * from "./cert-plugin/index.js";
export * from "./cert-plugin/lego/index.js";
export * from "./cert-convert/index.js";