package utils import ( "crypto/x509" "testing" "time" "git.secnex.io/secnex/certman/models" ) func TestDefaultCertificateConfig(t *testing.T) { tests := []struct { certType models.CertificateType expected bool }{ {models.CertificateTypeCA, true}, {models.CertificateTypeWeb, true}, {models.CertificateTypeClient, true}, {models.CertificateTypeEmail, true}, {models.CertificateTypeCode, true}, {models.CertificateTypeServer, true}, } for _, test := range tests { config := DefaultCertificateConfig(test.certType) if config.CertificateType != test.certType { t.Errorf("Expected certificate type %s, got %s", test.certType, config.CertificateType) } if config.SerialNumber == nil { t.Error("Serial number should not be nil") } if config.NotBefore.After(config.NotAfter) { t.Error("NotBefore should be before NotAfter") } } } func TestCACertificateConfig(t *testing.T) { // Test root CA rootConfig := CACertificateConfig(true) if !rootConfig.IsCA { t.Error("Root CA should have IsCA = true") } if !rootConfig.KeyCertSign { t.Error("Root CA should have KeyCertSign = true") } if !rootConfig.CRLSign { t.Error("Root CA should have CRLSign = true") } if rootConfig.MaxPathLen != -1 { t.Error("Root CA should have MaxPathLen = -1") } // Test intermediate CA intermediateConfig := CACertificateConfig(false) if !intermediateConfig.IsCA { t.Error("Intermediate CA should have IsCA = true") } if !intermediateConfig.KeyCertSign { t.Error("Intermediate CA should have KeyCertSign = true") } if !intermediateConfig.CRLSign { t.Error("Intermediate CA should have CRLSign = true") } if intermediateConfig.MaxPathLen != 0 { t.Error("Intermediate CA should have MaxPathLen = 0") } } func TestWebServerCertificateConfig(t *testing.T) { domains := []string{"example.com", "www.example.com"} config := WebServerCertificateConfig(domains) if config.CertificateType != models.CertificateTypeWeb { t.Error("Should be web certificate type") } if !config.DigitalSignature { t.Error("Should have digital signature") } if !config.KeyEncipherment { t.Error("Should have key encipherment") } if !config.ServerAuth { t.Error("Should have server auth") } if len(config.DNSNames) != len(domains) { t.Error("DNS names should match input domains") } } func TestClientCertificateConfig(t *testing.T) { config := ClientCertificateConfig() if config.CertificateType != models.CertificateTypeClient { t.Error("Should be client certificate type") } if !config.DigitalSignature { t.Error("Should have digital signature") } if !config.KeyEncipherment { t.Error("Should have key encipherment") } if !config.ClientAuth { t.Error("Should have client auth") } } func TestEmailCertificateConfig(t *testing.T) { email := "test@example.com" config := EmailCertificateConfig(email) if config.CertificateType != models.CertificateTypeEmail { t.Error("Should be email certificate type") } if !config.DigitalSignature { t.Error("Should have digital signature") } if !config.KeyEncipherment { t.Error("Should have key encipherment") } if !config.EmailProtection { t.Error("Should have email protection") } if len(config.EmailAddresses) != 1 || config.EmailAddresses[0] != email { t.Error("Email addresses should match input") } } func TestCodeSigningCertificateConfig(t *testing.T) { config := CodeSigningCertificateConfig() if config.CertificateType != models.CertificateTypeCode { t.Error("Should be code signing certificate type") } if !config.DigitalSignature { t.Error("Should have digital signature") } if !config.CodeSigning { t.Error("Should have code signing") } } func TestGenerateKeyPair(t *testing.T) { tests := []struct { keyType KeyType keySize KeySize curve Curve }{ {KeyTypeRSA, KeySize2048, CurveP256}, {KeyTypeRSA, KeySize3072, CurveP256}, {KeyTypeRSA, KeySize4096, CurveP256}, {KeyTypeECDSA, KeySize2048, CurveP256}, {KeyTypeECDSA, KeySize2048, CurveP384}, {KeyTypeECDSA, KeySize2048, CurveP521}, } for _, test := range tests { config := DefaultCertificateConfig(models.CertificateTypeWeb) config.KeyType = test.keyType config.KeySize = test.keySize config.Curve = test.curve generator := NewCertificateGenerator(config) key, err := generator.GenerateKeyPair() if err != nil { t.Errorf("Failed to generate key pair for %s: %v", test.keyType, err) } if key == nil { t.Errorf("Generated key should not be nil for %s", test.keyType) } } } func TestGenerateSelfSignedCertificate(t *testing.T) { config := CACertificateConfig(true) config.CommonName = "Test Root CA" config.Organization = "Test Org" config.Country = "DE" generator := NewCertificateGenerator(config) cert, privateKey, err := generator.GenerateSelfSignedCertificate() if err != nil { t.Fatalf("Failed to generate self-signed certificate: %v", err) } if cert == nil { t.Error("Certificate should not be nil") } if privateKey == nil { t.Error("Private key should not be nil") } if !cert.IsCA { t.Error("CA certificate should have IsCA = true") } if cert.Subject.CommonName != config.CommonName { t.Error("Certificate subject should match configuration") } } func TestGenerateCertificate(t *testing.T) { // Generate root CA first rootConfig := CACertificateConfig(true) rootConfig.CommonName = "Test Root CA" rootConfig.Organization = "Test Org" rootConfig.Country = "DE" rootGenerator := NewCertificateGenerator(rootConfig) rootCert, rootPrivateKey, err := rootGenerator.GenerateSelfSignedCertificate() if err != nil { t.Fatalf("Failed to generate root CA: %v", err) } // Generate intermediate CA intermediateConfig := CACertificateConfig(false) intermediateConfig.CommonName = "Test Intermediate CA" intermediateConfig.Organization = "Test Org" intermediateConfig.Country = "DE" intermediateGenerator := NewCertificateGenerator(intermediateConfig) intermediateCert, intermediatePrivateKey, err := intermediateGenerator.GenerateCertificate(rootCert, rootPrivateKey) if err != nil { t.Fatalf("Failed to generate intermediate CA: %v", err) } if intermediateCert == nil { t.Error("Intermediate certificate should not be nil") } if intermediatePrivateKey == nil { t.Error("Intermediate private key should not be nil") } if !intermediateCert.IsCA { t.Error("Intermediate CA certificate should have IsCA = true") } if intermediateCert.Issuer.CommonName != rootCert.Subject.CommonName { t.Error("Intermediate certificate issuer should match root certificate subject") } // Generate end entity certificate endConfig := WebServerCertificateConfig([]string{"example.com"}) endConfig.CommonName = "example.com" endConfig.Organization = "Test Org" endConfig.Country = "DE" endGenerator := NewCertificateGenerator(endConfig) endCert, endPrivateKey, err := endGenerator.GenerateCertificate(intermediateCert, intermediatePrivateKey) if err != nil { t.Fatalf("Failed to generate end entity certificate: %v", err) } if endCert == nil { t.Error("End entity certificate should not be nil") } if endPrivateKey == nil { t.Error("End entity private key should not be nil") } if endCert.IsCA { t.Error("End entity certificate should not have IsCA = true") } if endCert.Issuer.CommonName != intermediateCert.Subject.CommonName { t.Error("End entity certificate issuer should match intermediate certificate subject") } } func TestValidateCertificateConfig(t *testing.T) { tests := []struct { name string config *CertificateConfig wantErr bool }{ { name: "valid config", config: &CertificateConfig{ CommonName: "example.com", Organization: "Test Org", Country: "DE", NotBefore: time.Now(), NotAfter: time.Now().AddDate(1, 0, 0), }, wantErr: false, }, { name: "missing common name", config: &CertificateConfig{ Organization: "Test Org", Country: "DE", NotBefore: time.Now(), NotAfter: time.Now().AddDate(1, 0, 0), }, wantErr: true, }, { name: "missing organization", config: &CertificateConfig{ CommonName: "example.com", Country: "DE", NotBefore: time.Now(), NotAfter: time.Now().AddDate(1, 0, 0), }, wantErr: true, }, { name: "missing country", config: &CertificateConfig{ CommonName: "example.com", Organization: "Test Org", NotBefore: time.Now(), NotAfter: time.Now().AddDate(1, 0, 0), }, wantErr: true, }, { name: "invalid date range", config: &CertificateConfig{ CommonName: "example.com", Organization: "Test Org", Country: "DE", NotBefore: time.Now().AddDate(1, 0, 0), NotAfter: time.Now(), }, wantErr: true, }, { name: "CA without key cert sign", config: &CertificateConfig{ CommonName: "example.com", Organization: "Test Org", Country: "DE", NotBefore: time.Now(), NotAfter: time.Now().AddDate(1, 0, 0), IsCA: true, KeyCertSign: false, }, wantErr: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { err := ValidateCertificateConfig(test.config) if (err != nil) != test.wantErr { t.Errorf("ValidateCertificateConfig() error = %v, wantErr %v", err, test.wantErr) } }) } } func TestParseSANs(t *testing.T) { tests := []struct { input string expectedDNS []string expectedIPs int expectedEmails []string expectedURIs []string }{ { input: "example.com,www.example.com", expectedDNS: []string{"example.com", "www.example.com"}, expectedIPs: 0, expectedEmails: []string{}, expectedURIs: []string{}, }, { input: "example.com,192.168.1.1,user@example.com,https://example.com", expectedDNS: []string{"example.com"}, expectedIPs: 1, expectedEmails: []string{"user@example.com"}, expectedURIs: []string{"https://example.com"}, }, { input: "", expectedDNS: []string{}, expectedIPs: 0, expectedEmails: []string{}, expectedURIs: []string{}, }, } for _, test := range tests { dnsNames, ipAddresses, emailAddresses, uris := ParseSANs(test.input) if len(dnsNames) != len(test.expectedDNS) { t.Errorf("Expected %d DNS names, got %d", len(test.expectedDNS), len(dnsNames)) } if len(ipAddresses) != test.expectedIPs { t.Errorf("Expected %d IP addresses, got %d", test.expectedIPs, len(ipAddresses)) } if len(emailAddresses) != len(test.expectedEmails) { t.Errorf("Expected %d email addresses, got %d", len(test.expectedEmails), len(emailAddresses)) } if len(uris) != len(test.expectedURIs) { t.Errorf("Expected %d URIs, got %d", len(test.expectedURIs), len(uris)) } } } func TestExportCertificateToPEM(t *testing.T) { // Generate a test certificate config := CACertificateConfig(true) config.CommonName = "Test CA" config.Organization = "Test Org" config.Country = "DE" generator := NewCertificateGenerator(config) cert, _, err := generator.GenerateSelfSignedCertificate() if err != nil { t.Fatalf("Failed to generate certificate: %v", err) } exporter := NewCertificateExporter() pemData, err := exporter.ExportCertificateToPEM(cert) if err != nil { t.Fatalf("Failed to export certificate to PEM: %v", err) } if len(pemData) == 0 { t.Error("PEM data should not be empty") } // Verify PEM format if !contains(pemData, []byte("-----BEGIN CERTIFICATE-----")) { t.Error("PEM data should contain certificate header") } if !contains(pemData, []byte("-----END CERTIFICATE-----")) { t.Error("PEM data should contain certificate footer") } } func TestExportPrivateKeyToPEM(t *testing.T) { // Generate a test certificate with private key config := CACertificateConfig(true) config.CommonName = "Test CA" config.Organization = "Test Org" config.Country = "DE" generator := NewCertificateGenerator(config) _, privateKey, err := generator.GenerateSelfSignedCertificate() if err != nil { t.Fatalf("Failed to generate certificate: %v", err) } exporter := NewCertificateExporter() pemData, err := exporter.ExportPrivateKeyToPEM(privateKey) if err != nil { t.Fatalf("Failed to export private key to PEM: %v", err) } if len(pemData) == 0 { t.Error("PEM data should not be empty") } // Verify PEM format if !contains(pemData, []byte("-----BEGIN")) { t.Error("PEM data should contain private key header") } if !contains(pemData, []byte("-----END")) { t.Error("PEM data should contain private key footer") } } func TestExportCertificateToDER(t *testing.T) { // Generate a test certificate config := CACertificateConfig(true) config.CommonName = "Test CA" config.Organization = "Test Org" config.Country = "DE" generator := NewCertificateGenerator(config) cert, _, err := generator.GenerateSelfSignedCertificate() if err != nil { t.Fatalf("Failed to generate certificate: %v", err) } exporter := NewCertificateExporter() derData := exporter.ExportCertificateToDER(cert) if len(derData) == 0 { t.Error("DER data should not be empty") } // Verify DER format by parsing it _, err = x509.ParseCertificate(derData) if err != nil { t.Errorf("DER data should be valid certificate: %v", err) } } func TestExportPrivateKeyToDER(t *testing.T) { // Generate a test certificate with private key config := CACertificateConfig(true) config.CommonName = "Test CA" config.Organization = "Test Org" config.Country = "DE" generator := NewCertificateGenerator(config) _, privateKey, err := generator.GenerateSelfSignedCertificate() if err != nil { t.Fatalf("Failed to generate certificate: %v", err) } exporter := NewCertificateExporter() derData, err := exporter.ExportPrivateKeyToDER(privateKey) if err != nil { t.Fatalf("Failed to export private key to DER: %v", err) } if len(derData) == 0 { t.Error("DER data should not be empty") } } // Helper function to check if a slice contains a subslice func contains(s, subslice []byte) bool { if len(subslice) > len(s) { return false } for i := 0; i <= len(s)-len(subslice); i++ { if bytesEqual(s[i:i+len(subslice)], subslice) { return true } } return false } func bytesEqual(a, b []byte) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true }