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,9 +1,9 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { buildChains } from './cert-chain';
|
||||
import { buildChains, matchKeys } from './cert-chain';
|
||||
import { detectBlob } from './cert-model';
|
||||
import type { CertItem } from './cert-types';
|
||||
import type { AnyItem, CertItem, CsrItem, KeyItem } from './cert-types';
|
||||
|
||||
const fx = (name: string) => readFileSync(join(__dirname, '__fixtures__', name));
|
||||
|
||||
@@ -150,3 +150,75 @@ describe('buildChains', () => {
|
||||
expect(buildChains([])).toEqual({ chains: [] });
|
||||
});
|
||||
});
|
||||
|
||||
describe('matchKeys', () => {
|
||||
const PASSWORD = 'Test-Pass-123';
|
||||
function all(...names: [string, string?][]): AnyItem[] {
|
||||
const items: AnyItem[] = [];
|
||||
names.forEach(([name, password], file) => {
|
||||
const result = detectBlob(fx(name), {
|
||||
file,
|
||||
path: name,
|
||||
passwords: password ? [password] : [],
|
||||
ownPassword: password ?? '',
|
||||
});
|
||||
items.push(...result.items);
|
||||
});
|
||||
return items;
|
||||
}
|
||||
const certsOf = (items: AnyItem[]) =>
|
||||
items.filter((i): i is CertItem => i.kind === 'certificate');
|
||||
const keysOf = (items: AnyItem[]) => items.filter((i): i is KeyItem => i.kind === 'privateKey');
|
||||
const csrsOf = (items: AnyItem[]) => items.filter((i): i is CsrItem => i.kind === 'csr');
|
||||
|
||||
it('ordnet Schluessel und Anfrage dem Serverzertifikat zu (RSA und EC)', () => {
|
||||
const items = all(
|
||||
['rsa-leaf.pem'],
|
||||
['rsa-leaf-key-enc-trad.pem', PASSWORD],
|
||||
['rsa-leaf.csr'],
|
||||
['ec-leaf.pem'],
|
||||
['ec-leaf-key-sec1.der'],
|
||||
['ec-leaf.csr.der'],
|
||||
);
|
||||
matchKeys(certsOf(items), keysOf(items), csrsOf(items));
|
||||
const certs = certsOf(items);
|
||||
const rsa = certs.find((c) => c.cn === 'www.example.test') as CertItem;
|
||||
const ec = certs.find((c) => c.cn === 'ec.example.test') as CertItem;
|
||||
const rsaKey = keysOf(items).find((k) => k.keyType === 'RSA') as KeyItem;
|
||||
const ecKey = keysOf(items).find((k) => k.keyType === 'EC') as KeyItem;
|
||||
const rsaCsr = csrsOf(items).find((r) => r.keyType === 'RSA') as CsrItem;
|
||||
const ecCsr = csrsOf(items).find((r) => r.keyType === 'EC') as CsrItem;
|
||||
expect(rsa.keyId).toBe(rsaKey.id);
|
||||
expect(rsaKey.certIds).toEqual([rsa.id]);
|
||||
expect(rsa.csrIds).toEqual([rsaCsr.id]);
|
||||
expect(rsaCsr.keyId).toBe(rsaKey.id);
|
||||
expect(rsaCsr.certIds).toEqual([rsa.id]);
|
||||
expect(ec.keyId).toBe(ecKey.id);
|
||||
expect(ecKey.certIds).toEqual([ec.id]);
|
||||
expect(ecCsr.keyId).toBe(ecKey.id);
|
||||
expect(ecCsr.certIds).toEqual([ec.id]);
|
||||
});
|
||||
|
||||
it('ein fremder Schluessel gehoert zu keinem Zertifikat', () => {
|
||||
const items = all(['rsa-leaf.pem'], ['ec-leaf-key.pem']);
|
||||
matchKeys(certsOf(items), keysOf(items), csrsOf(items));
|
||||
expect(certsOf(items)[0].keyId).toBeNull();
|
||||
expect(keysOf(items)[0].certIds).toEqual([]);
|
||||
});
|
||||
|
||||
it('Anfrage ohne Zertifikat und ohne Schluessel bleibt ohne Zuordnung', () => {
|
||||
const items = all(['ec-leaf.csr']);
|
||||
matchKeys(certsOf(items), keysOf(items), csrsOf(items));
|
||||
expect(csrsOf(items)[0].keyId).toBeNull();
|
||||
expect(csrsOf(items)[0].certIds).toEqual([]);
|
||||
});
|
||||
|
||||
it('Zwischenzertifikat und Wurzel bekommen nie einen Schluessel, die Zuordnung ist wiederholbar', () => {
|
||||
const items = all(['rsa-leaf.pem'], ['rsa-inter.pem'], ['rsa-leaf-key.pem']);
|
||||
matchKeys(certsOf(items), keysOf(items), csrsOf(items));
|
||||
matchKeys(certsOf(items), keysOf(items), csrsOf(items));
|
||||
const withKey = certsOf(items).filter((c) => c.keyId !== null);
|
||||
expect(withKey.map((c) => c.cn)).toEqual(['www.example.test']);
|
||||
expect(keysOf(items)[0].certIds).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user