feat: implement certificate validation and expiry detection (Phase 2.1)
Add comprehensive certificate metadata parsing and expiry warnings. ## Changes ### Types (src/types.ts) - Add CertificateInfo interface with validity dates, expiry status, issuer/subject, fingerprint ### Crypto Module (src/lib/crypto.ts) - Add parseCertificateInfo() to extract certificate metadata - Add isCertificateExpiringSoon() helper (default 30 days threshold) - Calculate SHA-256 fingerprint, parse DN fields, compute days until expiry ### SealingKeysView (src/components/SealingKeysView.tsx) - Display certificate expiry information in table - Show visual indicators: Expired (red), Expiring Soon (warning), Valid (normal) - Display days remaining for expiring certificates ### EncryptDialog (src/components/EncryptDialog.tsx) - Add expiry warning before encryption - Warn if certificate expired or expiring within 30 days - Show specific expiry date in warning message ## Features - **Certificate Parsing:** Extract all metadata from X.509 certificates - **Expiry Detection:** Automatic detection of expired/expiring certificates - **Visual Indicators:** Color-coded chips for expiry status - **Proactive Warnings:** Alert users before creating secrets with expiring certs - **SHA-256 Fingerprint:** Unique certificate identification ## Verification - TypeScript: 0 errors - Linting: 0 errors - Build: Success (343.95 kB, 94.58 kB gzipped) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
import forge from 'node-forge';
|
||||
import {
|
||||
Base64String,
|
||||
CertificateInfo,
|
||||
Err,
|
||||
Ok,
|
||||
PEMCertificate,
|
||||
@@ -154,3 +155,68 @@ export function validateCertificate(pemCert: PEMCertificate): boolean {
|
||||
const result = parsePublicKeyFromCert(pemCert);
|
||||
return result.ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse certificate and extract metadata
|
||||
*
|
||||
* Extracts validity dates, issuer/subject information, and calculates
|
||||
* expiration status and fingerprint.
|
||||
*
|
||||
* @param pemCert PEM-encoded certificate string (branded type)
|
||||
* @returns Result containing certificate information or error message
|
||||
*/
|
||||
export function parseCertificateInfo(pemCert: PEMCertificate): Result<CertificateInfo, string> {
|
||||
try {
|
||||
const cert = forge.pki.certificateFromPem(pemCert);
|
||||
const now = new Date();
|
||||
|
||||
// Extract validity dates
|
||||
const validFrom = cert.validity.notBefore;
|
||||
const validTo = cert.validity.notAfter;
|
||||
|
||||
// Calculate expiration status
|
||||
const isExpired = now > validTo;
|
||||
const daysUntilExpiry = Math.floor((validTo.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
||||
|
||||
// Format issuer and subject
|
||||
const formatDN = (attributes: forge.pki.CertificateField[]): string => {
|
||||
return attributes.map(a => `${a.shortName}=${a.value}`).join(', ');
|
||||
};
|
||||
|
||||
const issuer = formatDN(cert.issuer.attributes);
|
||||
const subject = formatDN(cert.subject.attributes);
|
||||
|
||||
// Calculate SHA-256 fingerprint
|
||||
const der = forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes();
|
||||
const md = forge.md.sha256.create();
|
||||
md.update(der);
|
||||
const fingerprint = md.digest().toHex().toUpperCase();
|
||||
|
||||
// Get serial number
|
||||
const serialNumber = cert.serialNumber;
|
||||
|
||||
return Ok({
|
||||
validFrom,
|
||||
validTo,
|
||||
isExpired,
|
||||
daysUntilExpiry,
|
||||
issuer,
|
||||
subject,
|
||||
fingerprint,
|
||||
serialNumber,
|
||||
});
|
||||
} catch (error) {
|
||||
return Err(`Failed to parse certificate info: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if certificate will expire soon (within threshold)
|
||||
*
|
||||
* @param info Certificate information
|
||||
* @param daysThreshold Number of days to consider "expiring soon" (default: 30)
|
||||
* @returns true if certificate will expire within threshold days
|
||||
*/
|
||||
export function isCertificateExpiringSoon(info: CertificateInfo, daysThreshold = 30): boolean {
|
||||
return !info.isExpired && info.daysUntilExpiry <= daysThreshold;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user