feat(cert-manager): Schlüssel, PFX und CSR erkennen, Passwort je Datei
- Private Schlüssel (PKCS#1, PKCS#8, SEC1; PEM und DER; unverschlüsselt, verschlüsselt, klassisch verschlüsselt) für RSA und EC über node:crypto - PKCS#12 lesen (OpenSSL 3, kompatibel, RC2; EC-Zertifikate und -Schlüssel), auch ohne Endung und im ZIP - Zertifikatsanfragen (CSR) als PEM und DER mit Inhaber, SAN und Schlüssel - Zuordnung von Schlüssel und Anfrage zum Zertifikat (checkPrivateKey, SPKI-Vergleich) - Feld passwords je Datei, gesperrte Dateien fragen nach dem Passwort; kein Passwort in Antwort oder Log - Oberfläche: Passwortfeld mit Anzeigen/Verbergen, Schlüssel- und Anfragekarten im Reiter Analysieren Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { X509Certificate } from 'node:crypto';
|
||||
import type { CertItem, ChainGap, ChainInfo } from './cert-types';
|
||||
import { createPrivateKey, createPublicKey, X509Certificate } from 'node:crypto';
|
||||
import { csrPublicKeyOf } from './cert-csr';
|
||||
import { spkiDerOf } from './cert-keys';
|
||||
import type { CertItem, ChainGap, ChainInfo, CsrItem, KeyItem } from './cert-types';
|
||||
|
||||
/**
|
||||
* Kettenbau des Zertifikat-Managers (quick-261009-ikt, D-18). Reine Funktionen, kein Netz.
|
||||
@@ -146,3 +148,69 @@ export function buildChains(certs: CertItem[], headIds?: string[]): { chains: Ch
|
||||
const now = Date.now();
|
||||
return { chains: heads.map((h) => chainOf(nodes, h, now)) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Ordnet Schluessel und Zertifikatsanfragen ihren Zertifikaten zu (D-18) und traegt die Zuordnung
|
||||
* in die Eintraege ein (`keyId`, `certIds`, `csrIds`); vorherige Zuordnungen werden ersetzt.
|
||||
*
|
||||
* - Schluessel und Zertifikat: `cert.checkPrivateKey(key)`.
|
||||
* - Anfrage und Zertifikat oder Schluessel: der oeffentliche Schluessel (SPKI-DER) ist derselbe.
|
||||
* Nie ueber Namen oder Modulus-Zeichenketten: das geht bei EC nicht und waere bei RSA unsauber.
|
||||
*/
|
||||
export function matchKeys(certs: CertItem[], keys: KeyItem[], csrs: CsrItem[]): void {
|
||||
const certObjects = certs.map((item) => {
|
||||
item.keyId = null;
|
||||
item.csrIds = [];
|
||||
try {
|
||||
const x = new X509Certificate(item.pem);
|
||||
return { item, x, spki: spkiDerOf(x.publicKey) };
|
||||
} catch {
|
||||
return { item, x: null, spki: null };
|
||||
}
|
||||
});
|
||||
const keyObjects = keys.map((item) => {
|
||||
item.certIds = [];
|
||||
try {
|
||||
const key = createPrivateKey(item.pem);
|
||||
return { item, key, spki: spkiDerOf(createPublicKey(key)) };
|
||||
} catch {
|
||||
return { item, key: null, spki: null };
|
||||
}
|
||||
});
|
||||
|
||||
for (const c of certObjects) {
|
||||
if (!c.x) continue;
|
||||
for (const k of keyObjects) {
|
||||
if (!k.key) continue;
|
||||
let matches = false;
|
||||
try {
|
||||
matches = c.x.checkPrivateKey(k.key);
|
||||
} catch {
|
||||
// nicht pruefbar (unbekannter Schluesseltyp): kein Treffer
|
||||
}
|
||||
if (matches) {
|
||||
c.item.keyId ??= k.item.id;
|
||||
k.item.certIds.push(c.item.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const csr of csrs) {
|
||||
csr.keyId = null;
|
||||
csr.certIds = [];
|
||||
let spki: Buffer;
|
||||
try {
|
||||
spki = csrPublicKeyOf(csr.pem).spki;
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
const key = keyObjects.find((k) => k.spki?.equals(spki));
|
||||
if (key) csr.keyId = key.item.id;
|
||||
for (const c of certObjects) {
|
||||
if (c.spki?.equals(spki)) {
|
||||
csr.certIds.push(c.item.id);
|
||||
c.item.csrIds.push(csr.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user