feat(M11c): crypto policy enforcement — CSR validation, MaxTTL caps, key metadata

Enforce certificate profile crypto constraints across all 5 issuance paths
(renewal, agent CSR, EST, SCEP). ValidateCSRAgainstProfile() rejects CSRs
with key algorithm/size that don't match profile rules. MaxTTL enforcement
caps certificate validity per issuer connector (Local CA, Vault, step-ca
enforce directly; ACME/DigiCert/Sectigo pass through). Key algorithm and
size are now persisted in certificate_versions for audit compliance.

16 new tests (12 service-layer + 4 Local CA connector). Removes hardcoded
version number from GUI sidebar. Documentation updated across architecture,
features, connectors, and README.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Shankar
2026-04-15 21:05:14 -04:00
parent e8ddd3c327
commit ff223e2586
22 changed files with 779 additions and 70 deletions
+6 -6
View File
@@ -140,7 +140,7 @@ func TestIssuerConnectorAdapter_IssueCertificate_Success(t *testing.T) {
adapter := NewIssuerConnectorAdapter(mock)
result, err := adapter.IssueCertificate(ctx, "example.com", []string{"www.example.com"}, "-----BEGIN CERTIFICATE REQUEST-----\nCSR\n-----END CERTIFICATE REQUEST-----", nil)
result, err := adapter.IssueCertificate(ctx, "example.com", []string{"www.example.com"}, "-----BEGIN CERTIFICATE REQUEST-----\nCSR\n-----END CERTIFICATE REQUEST-----", nil, 0)
if err != nil {
t.Fatalf("IssueCertificate failed: %v", err)
@@ -177,7 +177,7 @@ func TestIssuerConnectorAdapter_IssueCertificate_Error(t *testing.T) {
adapter := NewIssuerConnectorAdapter(mock)
result, err := adapter.IssueCertificate(ctx, "example.com", []string{}, "csr", nil)
result, err := adapter.IssueCertificate(ctx, "example.com", []string{}, "csr", nil, 0)
if err == nil {
t.Fatal("expected error, got nil")
@@ -211,7 +211,7 @@ func TestIssuerConnectorAdapter_IssueCertificate_RequestTranslation(t *testing.T
sans := []string{"www.test.example.com", "api.test.example.com"}
csrPEM := "-----BEGIN CERTIFICATE REQUEST-----\nCSR\n-----END CERTIFICATE REQUEST-----"
_, err := adapter.IssueCertificate(ctx, commonName, sans, csrPEM, nil)
_, err := adapter.IssueCertificate(ctx, commonName, sans, csrPEM, nil, 0)
if err != nil {
t.Fatalf("IssueCertificate failed: %v", err)
@@ -261,7 +261,7 @@ func TestIssuerConnectorAdapter_RenewCertificate_Success(t *testing.T) {
adapter := NewIssuerConnectorAdapter(mock)
result, err := adapter.RenewCertificate(ctx, "example.com", []string{"www.example.com"}, "-----BEGIN CERTIFICATE REQUEST-----\nCSR\n-----END CERTIFICATE REQUEST-----", nil)
result, err := adapter.RenewCertificate(ctx, "example.com", []string{"www.example.com"}, "-----BEGIN CERTIFICATE REQUEST-----\nCSR\n-----END CERTIFICATE REQUEST-----", nil, 0)
if err != nil {
t.Fatalf("RenewCertificate failed: %v", err)
@@ -298,7 +298,7 @@ func TestIssuerConnectorAdapter_RenewCertificate_Error(t *testing.T) {
adapter := NewIssuerConnectorAdapter(mock)
result, err := adapter.RenewCertificate(ctx, "example.com", []string{}, "csr", nil)
result, err := adapter.RenewCertificate(ctx, "example.com", []string{}, "csr", nil, 0)
if err == nil {
t.Fatal("expected error, got nil")
@@ -332,7 +332,7 @@ func TestIssuerConnectorAdapter_RenewCertificate_RequestTranslation(t *testing.T
sans := []string{"www.renew.example.com"}
csrPEM := "-----BEGIN CERTIFICATE REQUEST-----\nRENEW-CSR\n-----END CERTIFICATE REQUEST-----"
_, err := adapter.RenewCertificate(ctx, commonName, sans, csrPEM, nil)
_, err := adapter.RenewCertificate(ctx, commonName, sans, csrPEM, nil, 0)
if err != nil {
t.Fatalf("RenewCertificate failed: %v", err)