mirror of https://github.com/certd/certd
perf: 支持p7b证书格式
parent
70fcdc9ebb
commit
d9f4a5793d
|
@ -48,6 +48,7 @@ export type CertInfo = {
|
||||||
der?: string;
|
der?: string;
|
||||||
jks?: string;
|
jks?: string;
|
||||||
one?: string;
|
one?: string;
|
||||||
|
p7b?: string;
|
||||||
};
|
};
|
||||||
export type SSLProvider = "letsencrypt" | "google" | "zerossl";
|
export type SSLProvider = "letsencrypt" | "google" | "zerossl";
|
||||||
export type PrivateKeyType = "rsa_1024" | "rsa_2048" | "rsa_3072" | "rsa_4096" | "ec_256" | "ec_384" | "ec_521";
|
export type PrivateKeyType = "rsa_1024" | "rsa_2048" | "rsa_3072" | "rsa_4096" | "ec_256" | "ec_384" | "ec_521";
|
||||||
|
|
|
@ -125,6 +125,10 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||||
cert.jks = res.jks;
|
cert.jks = res.jks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cert.p7b == null && res.p7b) {
|
||||||
|
cert.p7b = res.p7b;
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.info("转换证书格式成功");
|
this.logger.info("转换证书格式成功");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error("转换证书格式失败", e);
|
this.logger.error("转换证书格式失败", e);
|
||||||
|
@ -150,6 +154,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||||
zip.file("intermediate.crt", cert.ic);
|
zip.file("intermediate.crt", cert.ic);
|
||||||
zip.file("origin.crt", cert.oc);
|
zip.file("origin.crt", cert.oc);
|
||||||
zip.file("one.pem", cert.one);
|
zip.file("one.pem", cert.one);
|
||||||
|
zip.file("cert.p7b", cert.p7b);
|
||||||
if (cert.pfx) {
|
if (cert.pfx) {
|
||||||
zip.file("cert.pfx", Buffer.from(cert.pfx, "base64"));
|
zip.file("cert.pfx", Buffer.from(cert.pfx, "base64"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ export type CertReaderHandleContext = {
|
||||||
tmpIcPath?: string;
|
tmpIcPath?: string;
|
||||||
tmpJksPath?: string;
|
tmpJksPath?: string;
|
||||||
tmpOnePath?: string;
|
tmpOnePath?: string;
|
||||||
|
tmpP7bPath?: string;
|
||||||
};
|
};
|
||||||
export type CertReaderHandle = (ctx: CertReaderHandleContext) => Promise<void>;
|
export type CertReaderHandle = (ctx: CertReaderHandleContext) => Promise<void>;
|
||||||
export type HandleOpts = { logger: ILogger; handle: CertReaderHandle };
|
export type HandleOpts = { logger: ILogger; handle: CertReaderHandle };
|
||||||
|
@ -124,7 +125,7 @@ export class CertReader {
|
||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveToFile(type: "crt" | "key" | "pfx" | "der" | "oc" | "one" | "ic" | "jks", filepath?: string) {
|
saveToFile(type: "crt" | "key" | "pfx" | "der" | "oc" | "one" | "ic" | "jks" | "p7b", filepath?: string) {
|
||||||
if (!this.cert[type]) {
|
if (!this.cert[type]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -138,7 +139,7 @@ export class CertReader {
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
}
|
}
|
||||||
if (type === "crt" || type === "key" || type === "ic" || type === "oc" || type === "one") {
|
if (type === "crt" || type === "key" || type === "ic" || type === "oc" || type === "one" || type === "p7b") {
|
||||||
fs.writeFileSync(filepath, this.cert[type]);
|
fs.writeFileSync(filepath, this.cert[type]);
|
||||||
} else {
|
} else {
|
||||||
fs.writeFileSync(filepath, Buffer.from(this.cert[type], "base64"));
|
fs.writeFileSync(filepath, Buffer.from(this.cert[type], "base64"));
|
||||||
|
@ -157,17 +158,19 @@ export class CertReader {
|
||||||
const tmpDerPath = this.saveToFile("der");
|
const tmpDerPath = this.saveToFile("der");
|
||||||
const tmpJksPath = this.saveToFile("jks");
|
const tmpJksPath = this.saveToFile("jks");
|
||||||
const tmpOnePath = this.saveToFile("one");
|
const tmpOnePath = this.saveToFile("one");
|
||||||
|
const tmpP7bPath = this.saveToFile("p7b");
|
||||||
logger.info("本地文件写入成功");
|
logger.info("本地文件写入成功");
|
||||||
try {
|
try {
|
||||||
return await opts.handle({
|
return await opts.handle({
|
||||||
reader: this,
|
reader: this,
|
||||||
tmpCrtPath: tmpCrtPath,
|
tmpCrtPath,
|
||||||
tmpKeyPath: tmpKeyPath,
|
tmpKeyPath,
|
||||||
tmpPfxPath: tmpPfxPath,
|
tmpPfxPath,
|
||||||
tmpDerPath: tmpDerPath,
|
tmpDerPath,
|
||||||
tmpIcPath: tmpIcPath,
|
tmpIcPath,
|
||||||
tmpJksPath: tmpJksPath,
|
tmpJksPath,
|
||||||
tmpOcPath: tmpOcPath,
|
tmpOcPath,
|
||||||
|
tmpP7bPath,
|
||||||
tmpOnePath,
|
tmpOnePath,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -189,6 +192,7 @@ export class CertReader {
|
||||||
removeFile(tmpIcPath);
|
removeFile(tmpIcPath);
|
||||||
removeFile(tmpJksPath);
|
removeFile(tmpJksPath);
|
||||||
removeFile(tmpOnePath);
|
removeFile(tmpOnePath);
|
||||||
|
removeFile(tmpP7bPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,13 @@ export class CertConverter {
|
||||||
pfx: string;
|
pfx: string;
|
||||||
der: string;
|
der: string;
|
||||||
jks: string;
|
jks: string;
|
||||||
|
p7b: string;
|
||||||
}> {
|
}> {
|
||||||
const certReader = new CertReader(opts.cert);
|
const certReader = new CertReader(opts.cert);
|
||||||
let pfx: string;
|
let pfx: string;
|
||||||
let der: string;
|
let der: string;
|
||||||
let jks: string;
|
let jks: string;
|
||||||
|
let p7b: string;
|
||||||
const handle = async (ctx: CertReaderHandleContext) => {
|
const handle = async (ctx: CertReaderHandleContext) => {
|
||||||
// 调用openssl 转pfx
|
// 调用openssl 转pfx
|
||||||
pfx = await this.convertPfx(ctx, opts.pfxPassword, opts.pfxArgs);
|
pfx = await this.convertPfx(ctx, opts.pfxPassword, opts.pfxArgs);
|
||||||
|
@ -31,6 +33,8 @@ export class CertConverter {
|
||||||
der = await this.convertDer(ctx);
|
der = await this.convertDer(ctx);
|
||||||
|
|
||||||
jks = await this.convertJks(ctx, opts.pfxPassword);
|
jks = await this.convertJks(ctx, opts.pfxPassword);
|
||||||
|
|
||||||
|
p7b = await this.convertP7b(ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
await certReader.readCertFile({ logger: this.logger, handle });
|
await certReader.readCertFile({ logger: this.logger, handle });
|
||||||
|
@ -39,6 +43,7 @@ export class CertConverter {
|
||||||
pfx,
|
pfx,
|
||||||
der,
|
der,
|
||||||
jks,
|
jks,
|
||||||
|
p7b,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +100,23 @@ export class CertConverter {
|
||||||
return derCert;
|
return derCert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async convertP7b(opts: CertReaderHandleContext) {
|
||||||
|
const { tmpCrtPath } = opts;
|
||||||
|
const p7bPath = path.join(os.tmpdir(), "/certd/tmp/", Math.floor(Math.random() * 1000000) + `_cert.p7b`);
|
||||||
|
const dir = path.dirname(p7bPath);
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
//openssl crl2pkcs7 -nocrl \
|
||||||
|
// -certfile your_domain.crt \
|
||||||
|
// -certfile intermediate.crt \
|
||||||
|
// -out chain.p7b
|
||||||
|
await this.exec(`openssl crl2pkcs7 -nocrl -certfile ${tmpCrtPath} -out ${p7bPath}`);
|
||||||
|
const fileBuffer = fs.readFileSync(p7bPath);
|
||||||
|
const p7bCert = fileBuffer.toString();
|
||||||
|
fs.unlinkSync(p7bPath);
|
||||||
|
return p7bCert;
|
||||||
|
}
|
||||||
async convertJks(opts: CertReaderHandleContext, pfxPassword = "") {
|
async convertJks(opts: CertReaderHandleContext, pfxPassword = "") {
|
||||||
const jksPassword = pfxPassword || "123456";
|
const jksPassword = pfxPassword || "123456";
|
||||||
try {
|
try {
|
||||||
|
@ -113,9 +135,7 @@ export class CertConverter {
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
}
|
}
|
||||||
await this.exec(
|
await this.exec(`keytool -importkeystore -srckeystore ${p12Path} -srcstoretype PKCS12 -srcstorepass "${jksPassword}" -destkeystore ${jksPath} -deststoretype PKCS12 -deststorepass "${jksPassword}" `);
|
||||||
`keytool -importkeystore -srckeystore ${p12Path} -srcstoretype PKCS12 -srcstorepass "${jksPassword}" -destkeystore ${jksPath} -deststoretype PKCS12 -deststorepass "${jksPassword}" `
|
|
||||||
);
|
|
||||||
fs.unlinkSync(p12Path);
|
fs.unlinkSync(p12Path);
|
||||||
|
|
||||||
const fileBuffer = fs.readFileSync(jksPath);
|
const fileBuffer = fs.readFileSync(jksPath);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<a-form-item v-if="formState.yizhifu.enabled" label="易支付配置" :name="['yizhifu', 'accessId']" :required="true">
|
<a-form-item v-if="formState.yizhifu.enabled" label="易支付配置" :name="['yizhifu', 'accessId']" :required="true">
|
||||||
<access-selector v-model="formState.yizhifu.accessId" type="yizhifu" from="sys" />
|
<access-selector v-model="formState.yizhifu.accessId" type="yizhifu" from="sys" />
|
||||||
<div class="helper">
|
<div class="helper">
|
||||||
<a href="https://certd.docmirror.cn/comm/payments/yizhifu.html">彩虹易支付配置帮助文档</a>
|
<a href="https://certd.docmirror.cn/guide/use/comm/payments/yizhifu.html">彩虹易支付配置帮助文档</a>
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="formState.alipay.enabled" label="支付宝配置" :name="['alipay', 'accessId']" :required="true">
|
<a-form-item v-if="formState.alipay.enabled" label="支付宝配置" :name="['alipay', 'accessId']" :required="true">
|
||||||
<access-selector v-model="formState.alipay.accessId" type="alipay" from="sys" />
|
<access-selector v-model="formState.alipay.accessId" type="alipay" from="sys" />
|
||||||
<div class="helper">需要开通电脑网站支付, <a href="https://certd.docmirror.cn/comm/payments/alipay.html">支付宝配置帮助文档</a></div>
|
<div class="helper">需要开通电脑网站支付, <a href="https://certd.docmirror.cn/guide/use/comm/payments/alipay.html">支付宝配置帮助文档</a></div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="微信支付" :name="['wxpay', 'enabled']" :required="true">
|
<a-form-item label="微信支付" :name="['wxpay', 'enabled']" :required="true">
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="formState.wxpay.enabled" label="微信支付配置" :name="['wxpay', 'accessId']" :required="true">
|
<a-form-item v-if="formState.wxpay.enabled" label="微信支付配置" :name="['wxpay', 'accessId']" :required="true">
|
||||||
<access-selector v-model="formState.wxpay.accessId" type="wxpay" from="sys" />
|
<access-selector v-model="formState.wxpay.accessId" type="wxpay" from="sys" />
|
||||||
<div class="helper">需要开通Native支付, <a href="https://certd.docmirror.cn/comm/payments/wxpay.html">微信配置帮助文档</a></div>
|
<div class="helper">需要开通Native支付, <a href="https://certd.docmirror.cn/guide/use/comm/payments/wxpay.html">微信配置帮助文档</a></div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
||||||
|
|
|
@ -39,6 +39,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
{ value: 'der', label: 'der,一般用于Apache' },
|
{ value: 'der', label: 'der,一般用于Apache' },
|
||||||
{ value: 'jks', label: 'jks,一般用于JAVA应用' },
|
{ value: 'jks', label: 'jks,一般用于JAVA应用' },
|
||||||
{ value: 'one', label: '证书私钥一体,crt+key简单合并为一个pem文件' },
|
{ value: 'one', label: '证书私钥一体,crt+key简单合并为一个pem文件' },
|
||||||
|
{ value: 'p7b', label: 'p7b格式' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -71,7 +72,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
mergeScript: `
|
mergeScript: `
|
||||||
return {
|
return {
|
||||||
show: ctx.compute(({form})=>{
|
show: ctx.compute(({form})=>{
|
||||||
return form.certType === 'pem';
|
return form.certType === 'pem' || form.certType === 'p7b' ;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@ -169,6 +170,24 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
})
|
})
|
||||||
onePath!: string;
|
onePath!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: 'p7b证书保存路径',
|
||||||
|
helper: '填写应用原本的证书保存路径,路径要包含证书文件名,例如:/tmp/domain_cert.p7b',
|
||||||
|
component: {
|
||||||
|
placeholder: '/root/deploy/app/domain_cert.p7b',
|
||||||
|
},
|
||||||
|
mergeScript: `
|
||||||
|
return {
|
||||||
|
show: ctx.compute(({form})=>{
|
||||||
|
return form.certType === 'p7b';
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
required: true,
|
||||||
|
rules: [{ type: 'filepath' }],
|
||||||
|
})
|
||||||
|
p7bPath!: string;
|
||||||
|
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: '主机登录配置',
|
title: '主机登录配置',
|
||||||
helper: 'access授权',
|
helper: 'access授权',
|
||||||
|
@ -277,12 +296,17 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
})
|
})
|
||||||
hostOnePath!: string;
|
hostOnePath!: string;
|
||||||
|
|
||||||
|
@TaskOutput({
|
||||||
|
title: 'p7b证书保存路径',
|
||||||
|
})
|
||||||
|
hostP7bPath!: string;
|
||||||
|
|
||||||
async onInstance() {}
|
async onInstance() {}
|
||||||
|
|
||||||
|
|
||||||
async execute(): Promise<void> {
|
async execute(): Promise<void> {
|
||||||
const { cert, accessId } = this;
|
const { cert, accessId } = this;
|
||||||
let { crtPath, keyPath, icPath, pfxPath, derPath, jksPath, onePath } = this;
|
let { crtPath, keyPath, icPath, pfxPath, derPath, jksPath, onePath,p7bPath } = this;
|
||||||
const certReader = new CertReader(cert);
|
const certReader = new CertReader(cert);
|
||||||
|
|
||||||
const executeCmd = async ( script:string)=> {
|
const executeCmd = async ( script:string)=> {
|
||||||
|
@ -308,6 +332,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
env['HOST_DER_PATH'] = this.hostDerPath || '';
|
env['HOST_DER_PATH'] = this.hostDerPath || '';
|
||||||
env['HOST_JKS_PATH'] = this.hostJksPath || '';
|
env['HOST_JKS_PATH'] = this.hostJksPath || '';
|
||||||
env['HOST_ONE_PATH'] = this.hostOnePath || '';
|
env['HOST_ONE_PATH'] = this.hostOnePath || '';
|
||||||
|
env['HOST_P7B_PATH'] = this.hostOnePath || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const scripts = script.split('\n');
|
const scripts = script.split('\n');
|
||||||
|
@ -320,7 +345,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handle = async (opts: CertReaderHandleContext) => {
|
const handle = async (opts: CertReaderHandleContext) => {
|
||||||
const { tmpCrtPath, tmpKeyPath, tmpDerPath, tmpJksPath, tmpPfxPath, tmpIcPath, tmpOnePath } = opts;
|
const { tmpCrtPath, tmpKeyPath, tmpDerPath, tmpJksPath, tmpPfxPath, tmpIcPath, tmpOnePath ,tmpP7bPath} = opts;
|
||||||
|
|
||||||
if (accessId == null) {
|
if (accessId == null) {
|
||||||
this.logger.error('复制到当前主机功能已迁移到 “复制到本机”插件,请换成复制到本机插件');
|
this.logger.error('复制到当前主机功能已迁移到 “复制到本机”插件,请换成复制到本机插件');
|
||||||
|
@ -392,6 +417,14 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
remotePath: this.onePath,
|
remotePath: this.onePath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (this.p7bPath) {
|
||||||
|
this.logger.info(`上传p7b证书到主机:${this.p7bPath}`);
|
||||||
|
p7bPath = this.p7bPath.trim();
|
||||||
|
transports.push({
|
||||||
|
localPath: tmpP7bPath,
|
||||||
|
remotePath: this.p7bPath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.info('开始上传文件到服务器');
|
this.logger.info('开始上传文件到服务器');
|
||||||
await sshClient.uploadFiles({
|
await sshClient.uploadFiles({
|
||||||
|
@ -410,6 +443,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
this.hostDerPath = derPath;
|
this.hostDerPath = derPath;
|
||||||
this.hostJksPath = jksPath;
|
this.hostJksPath = jksPath;
|
||||||
this.hostOnePath = onePath;
|
this.hostOnePath = onePath;
|
||||||
|
this.hostP7bPath = p7bPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
//执行前置命令
|
//执行前置命令
|
||||||
|
|
Loading…
Reference in New Issue