mirror of
https://github.com/certd/certd.git
synced 2026-04-03 14:10:54 +08:00
perf: 查看证书增加证书详情显示,包括域名,过期时间,颁发机构,指纹等
This commit is contained in:
@@ -2,6 +2,7 @@ import fs from "fs";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import { CertificateInfo, crypto } from "@certd/acme-client";
|
||||
import cryptoLib from "crypto";
|
||||
import { ILogger } from "@certd/basic";
|
||||
import dayjs from "dayjs";
|
||||
import { uniq } from "lodash-es";
|
||||
@@ -119,9 +120,28 @@ export class CertReader {
|
||||
const detail = crypto.readCertificateInfo(crt.toString());
|
||||
const effective = detail.notBefore;
|
||||
const expires = detail.notAfter;
|
||||
const fingerprints = CertReader.getFingerprintX509(crt);
|
||||
// @ts-ignore
|
||||
detail.fingerprints = fingerprints;
|
||||
return { detail, effective, expires };
|
||||
}
|
||||
|
||||
static getFingerprintX509(crt: string) {
|
||||
try {
|
||||
// 创建X509Certificate实例
|
||||
const cert = new cryptoLib.X509Certificate(crt);
|
||||
// 获取指纹
|
||||
return {
|
||||
fingerprint: cert.fingerprint,
|
||||
fingerprint256: cert.fingerprint256,
|
||||
fingerprint512: cert.fingerprint512,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("处理证书失败:", error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getAllDomains() {
|
||||
const { detail } = this.getCrtDetail();
|
||||
const domains = [];
|
||||
|
||||
@@ -154,6 +154,7 @@ export type CertInfo = {
|
||||
ic: string;
|
||||
der: string;
|
||||
pfx: string;
|
||||
detail: any;
|
||||
};
|
||||
|
||||
export async function GetCert(pipelineId: number): Promise<CertInfo> {
|
||||
|
||||
@@ -1,12 +1,47 @@
|
||||
<template>
|
||||
<div class="cert-view">
|
||||
<a-list item-layout="vertical" :data-source="certFiles">
|
||||
<div class="cert-detail mt-4">
|
||||
<a-descriptions class="w-full" bordered :column="2" size="small">
|
||||
<a-descriptions-item label="主域名">
|
||||
<a-tag color="blue">{{ props.cert.detail?.domains?.commonName || "-" }}</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="颁发机构">
|
||||
<a-tag color="green">{{ props.cert.detail?.issuer?.commonName || "-" }}</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="备用域名" :span="2">
|
||||
<a-tag v-for="(domain, index) in props.cert.detail?.domains?.altNames || []" :key="index" color="blue">
|
||||
{{ domain }}
|
||||
</a-tag>
|
||||
<span v-if="!props.cert.detail?.domains?.altNames?.length">-</span>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="生效时间">
|
||||
{{ formatDate(props.cert.detail?.notBefore) }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="过期时间">
|
||||
{{ formatDate(props.cert.detail?.notAfter) }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="指纹">
|
||||
<div class="w-full flex flex-col fingerprint">
|
||||
<div class="flex flex-nowrap">
|
||||
<span class="font-bold label">SHA-1:</span> <fs-copyable class="inline-flex overflow-ellipsis text" :model-value="props.cert.detail?.fingerprints?.fingerprint || '-'"></fs-copyable>
|
||||
</div>
|
||||
<div class="flex flex-nowrap mt-1">
|
||||
<span class="font-bold label">SHA-256:</span> <fs-copyable class="inline-flex overflow-ellipsis text" :model-value="props.cert.detail?.fingerprints?.fingerprint256 || '-'"></fs-copyable>
|
||||
</div>
|
||||
<div class="flex flex-nowrap mt-1">
|
||||
<span class="font-bold label">SHA-512:</span> <fs-copyable class="inline-flex overflow-ellipsis text" :model-value="props.cert.detail?.fingerprints?.fingerprint512 || '-'"></fs-copyable>
|
||||
</div>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
<a-list item-layout="vertical" :data-source="certFiles" class="cert-content">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item key="item.key">
|
||||
<a-list-item-meta>
|
||||
<template #title>
|
||||
<div class="title">
|
||||
<div>{{ item.name }}({{ item.fileName }})</div>
|
||||
<div class="font-bold">{{ item.name }}({{ item.fileName }})</div>
|
||||
<fs-copyable :model-value="item.content" :button="{ show: false }">
|
||||
<a-tag color="success">复制</a-tag>
|
||||
</fs-copyable>
|
||||
@@ -34,10 +69,25 @@ const certFiles = ref([
|
||||
{ name: "私钥", fileName: "private.pem", key: "key", content: props.cert.key },
|
||||
{ name: "中间证书", fileName: "chain.pem", key: "ic", content: props.cert.ic },
|
||||
]);
|
||||
|
||||
function formatDate(dateStr: string): string {
|
||||
if (!dateStr) return "-";
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString("zh-CN", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.cert-view {
|
||||
margin-right: 25px;
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -46,5 +96,29 @@ const certFiles = ref([
|
||||
margin-block-end: 0px !important;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.cert-detail {
|
||||
table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
.fingerprint {
|
||||
.label {
|
||||
width: 70px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cert-content {
|
||||
.ant-list-item {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { logger } from "@certd/basic";
|
||||
import fs from "fs";
|
||||
import dayjs from "dayjs";
|
||||
import { ApiTags } from "@midwayjs/swagger";
|
||||
import { CertReader } from "@certd/plugin-lib";
|
||||
|
||||
/**
|
||||
*/
|
||||
@@ -159,6 +160,10 @@ export class CertInfoController extends CrudController<CertInfoService> {
|
||||
await this.checkOwner(this.getService(),id,"read");
|
||||
const certInfoEntity = await this.service.info(id);
|
||||
const certInfo = JSON.parse(certInfoEntity.certInfo);
|
||||
if (certInfo?.crt) {
|
||||
const certReader = new CertReader(certInfo);
|
||||
certInfo.detail = certReader.detail
|
||||
}
|
||||
return this.ok(certInfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,14 @@ export class CertController extends BaseController {
|
||||
}
|
||||
}
|
||||
const privateVars = await this.storeService.getPipelinePrivateVars(id);
|
||||
return this.ok(privateVars.cert);
|
||||
|
||||
const certInfo = privateVars.cert;
|
||||
if (certInfo?.crt) {
|
||||
const certReader = new CertReader(certInfo);
|
||||
certInfo.detail = certReader.detail
|
||||
}
|
||||
|
||||
return this.ok(certInfo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user