Fix: encode IP addresses as raw octets in Subject Alternative Names (#116)

- Update `GenerateCertificate` to correctly identify IP addresses and
add them to `IPAddresses` instead of `DNSNames`.
- Update `GetServerTLSConfig` to verify both `DNSNames` and
`IPAddresses` when checking certificate validity.
- Add `TestCertificateManagerIPAddress` to `certmanager_test.go` to
ensure correct encoding and prevent regressions.
- Ensure compliance with RFC 5280 by using binary encoding for IP
addresses in certificates.
This commit is contained in:
Tobias Gesellchen
2026-03-17 09:33:51 +01:00
committed by GitHub
parent ad5344b309
commit 17bd3ea9ed
2 changed files with 64 additions and 1 deletions
+20 -1
View File
@@ -9,6 +9,7 @@ import (
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"net"
"os"
"path/filepath"
"time"
@@ -80,6 +81,10 @@ func (cm *CertificateManager) GetServerTLSConfig(domains []string) (*tls.Config,
domainMap[d] = true
}
for _, ip := range cert.IPAddresses {
domainMap[ip.String()] = true
}
for _, d := range domains {
if !domainMap[d] {
generate = true
@@ -237,6 +242,19 @@ func (cm *CertificateManager) GenerateCertificate(domains []string) ([]byte, []b
return nil, nil, err
}
var (
dnsNames []string
ipAddresses []net.IP
)
for _, domain := range domains {
if ip := net.ParseIP(domain); ip != nil {
ipAddresses = append(ipAddresses, ip)
} else {
dnsNames = append(dnsNames, domain)
}
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
@@ -247,7 +265,8 @@ func (cm *CertificateManager) GenerateCertificate(domains []string) ([]byte, []b
NotAfter: notAfter,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
DNSNames: domains,
DNSNames: dnsNames,
IPAddresses: ipAddresses,
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, &priv.PublicKey, caKey)
@@ -126,3 +126,47 @@ func TestCertificateManager(t *testing.T) {
}
}
}
func TestCertificateManagerIPAddress(t *testing.T) {
tempDir, err := os.MkdirTemp("", "crypto-ip-test")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)
cm := NewCertificateManager(filepath.Join(tempDir, "certs"))
// Test certificate generation with an IP address
domains := []string{"192.168.1.100", "localhost"}
certPEM, _, err := cm.GenerateCertificate(domains)
if err != nil {
t.Fatalf("Failed to generate certificate: %v", err)
}
// Verify generated certificate
block, _ := pem.Decode(certPEM)
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
t.Fatalf("Failed to parse certificate: %v", err)
}
// Check IP addresses
foundIP := false
for _, ip := range cert.IPAddresses {
if ip.String() == "192.168.1.100" {
foundIP = true
break
}
}
if !foundIP {
t.Errorf("Expected IP address 192.168.1.100 in IPAddresses, but it was not found")
}
// Check if it was mistakenly added to DNSNames
for _, dns := range cert.DNSNames {
if dns == "192.168.1.100" {
t.Errorf("IP address 192.168.1.100 should NOT be in DNSNames")
}
}
}