修复托管证书链重复校验
Some checks are pending
Security Checks / security (push) Waiting to run

This commit is contained in:
smail 2026-09-06 09:06:02 +08:00
parent b3eef54489
commit 391f6bfed6
2 changed files with 74 additions and 1 deletions

View File

@ -227,7 +227,7 @@ func validateSSLMaterial(privateKeyPEM, certificatePEM, issuerPEM []byte, domain
if issuerErr != nil {
return ErrInvalidSSLMaterial
}
certificates = append(certificates, issuerCertificates...)
certificates = appendUniqueCertificates(certificates, issuerCertificates...)
}
if _, err := tls.X509KeyPair(certificatePEM, privateKeyPEM); err != nil {
return ErrSSLCertificateMismatch
@ -255,6 +255,28 @@ func validateSSLMaterial(privateKeyPEM, certificatePEM, issuerPEM []byte, domain
return nil
}
func appendUniqueCertificates(certificates []*x509.Certificate, additions ...*x509.Certificate) []*x509.Certificate {
seen := make(map[string]struct{}, len(certificates)+len(additions))
for _, certificate := range certificates {
if certificate == nil {
continue
}
seen[string(certificate.Raw)] = struct{}{}
}
for _, certificate := range additions {
if certificate == nil {
continue
}
key := string(certificate.Raw)
if _, exists := seen[key]; exists {
continue
}
seen[key] = struct{}{}
certificates = append(certificates, certificate)
}
return certificates
}
func parseCertificatePEM(data []byte) ([]*x509.Certificate, error) {
var certificates []*x509.Certificate
remaining := data

View File

@ -203,6 +203,14 @@ func TestValidateSSLMaterialRejectsDomainExpiryAndWeakKeys(t *testing.T) {
}
}
func TestValidateSSLMaterialAcceptsBundledIssuerWithoutDuplicateChainEntry(t *testing.T) {
privateKeyPEM, certificatePEM, issuerPEM := testChainedCertificate(t)
certificateBundle := append(append([]byte{}, certificatePEM...), issuerPEM...)
if err := ValidateSSLMaterial(privateKeyPEM, certificateBundle, issuerPEM, []string{"mail.example.com"}); err != nil {
t.Fatalf("bundled issuer was rejected as a duplicate chain entry: %v", err)
}
}
func testManagedCertificate(t *testing.T, domain string, notBefore, notAfter time.Time, bits int) ([]byte, []byte) {
t.Helper()
key, err := rsa.GenerateKey(rand.Reader, bits)
@ -226,3 +234,46 @@ func testManagedCertificate(t *testing.T, domain string, notBefore, notAfter tim
}
return pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}), pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
}
func testChainedCertificate(t *testing.T) ([]byte, []byte, []byte) {
t.Helper()
issuerKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
now := time.Now()
issuerTemplate := &x509.Certificate{
SerialNumber: big.NewInt(100),
Subject: pkix.Name{CommonName: "Smail Test Issuer"},
NotBefore: now.Add(-time.Hour),
NotAfter: now.Add(24 * time.Hour),
BasicConstraintsValid: true,
IsCA: true,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
}
issuerDER, err := x509.CreateCertificate(rand.Reader, issuerTemplate, issuerTemplate, &issuerKey.PublicKey, issuerKey)
if err != nil {
t.Fatal(err)
}
leafKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
leafTemplate := &x509.Certificate{
SerialNumber: big.NewInt(101),
Subject: pkix.Name{CommonName: "mail.example.com"},
DNSNames: []string{"mail.example.com"},
NotBefore: now.Add(-time.Hour),
NotAfter: now.Add(24 * time.Hour),
BasicConstraintsValid: true,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
leafDER, err := x509.CreateCertificate(rand.Reader, leafTemplate, issuerTemplate, &leafKey.PublicKey, issuerKey)
if err != nil {
t.Fatal(err)
}
return pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(leafKey)}),
pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafDER}),
pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: issuerDER})
}