mirror of
https://github.com/certd/certd.git
synced 2026-04-24 20:57:26 +08:00
perf: 查看证书增加证书详情显示,包括域名,过期时间,颁发机构,指纹等
This commit is contained in:
@@ -2,6 +2,7 @@ import fs from "fs";
|
|||||||
import os from "os";
|
import os from "os";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { CertificateInfo, crypto } from "@certd/acme-client";
|
import { CertificateInfo, crypto } from "@certd/acme-client";
|
||||||
|
import cryptoLib from "crypto";
|
||||||
import { ILogger } from "@certd/basic";
|
import { ILogger } from "@certd/basic";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { uniq } from "lodash-es";
|
import { uniq } from "lodash-es";
|
||||||
@@ -119,9 +120,28 @@ export class CertReader {
|
|||||||
const detail = crypto.readCertificateInfo(crt.toString());
|
const detail = crypto.readCertificateInfo(crt.toString());
|
||||||
const effective = detail.notBefore;
|
const effective = detail.notBefore;
|
||||||
const expires = detail.notAfter;
|
const expires = detail.notAfter;
|
||||||
|
const fingerprints = CertReader.getFingerprintX509(crt);
|
||||||
|
// @ts-ignore
|
||||||
|
detail.fingerprints = fingerprints;
|
||||||
return { detail, effective, expires };
|
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() {
|
getAllDomains() {
|
||||||
const { detail } = this.getCrtDetail();
|
const { detail } = this.getCrtDetail();
|
||||||
const domains = [];
|
const domains = [];
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ export type CertInfo = {
|
|||||||
ic: string;
|
ic: string;
|
||||||
der: string;
|
der: string;
|
||||||
pfx: string;
|
pfx: string;
|
||||||
|
detail: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function GetCert(pipelineId: number): Promise<CertInfo> {
|
export async function GetCert(pipelineId: number): Promise<CertInfo> {
|
||||||
|
|||||||
@@ -1,12 +1,47 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cert-view">
|
<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 }">
|
<template #renderItem="{ item }">
|
||||||
<a-list-item key="item.key">
|
<a-list-item key="item.key">
|
||||||
<a-list-item-meta>
|
<a-list-item-meta>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="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 }">
|
<fs-copyable :model-value="item.content" :button="{ show: false }">
|
||||||
<a-tag color="success">复制</a-tag>
|
<a-tag color="success">复制</a-tag>
|
||||||
</fs-copyable>
|
</fs-copyable>
|
||||||
@@ -34,10 +69,25 @@ const certFiles = ref([
|
|||||||
{ name: "私钥", fileName: "private.pem", key: "key", content: props.cert.key },
|
{ name: "私钥", fileName: "private.pem", key: "key", content: props.cert.key },
|
||||||
{ name: "中间证书", fileName: "chain.pem", key: "ic", content: props.cert.ic },
|
{ 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>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.cert-view {
|
.cert-view {
|
||||||
|
margin-right: 25px;
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -46,5 +96,29 @@ const certFiles = ref([
|
|||||||
margin-block-end: 0px !important;
|
margin-block-end: 0px !important;
|
||||||
margin-top: 10px;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { logger } from "@certd/basic";
|
|||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { ApiTags } from "@midwayjs/swagger";
|
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");
|
await this.checkOwner(this.getService(),id,"read");
|
||||||
const certInfoEntity = await this.service.info(id);
|
const certInfoEntity = await this.service.info(id);
|
||||||
const certInfo = JSON.parse(certInfoEntity.certInfo);
|
const certInfo = JSON.parse(certInfoEntity.certInfo);
|
||||||
|
if (certInfo?.crt) {
|
||||||
|
const certReader = new CertReader(certInfo);
|
||||||
|
certInfo.detail = certReader.detail
|
||||||
|
}
|
||||||
return this.ok(certInfo);
|
return this.ok(certInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,14 @@ export class CertController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const privateVars = await this.storeService.getPipelinePrivateVars(id);
|
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