104 lines
4.0 KiB
Go
104 lines
4.0 KiB
Go
package certificate
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"math/big"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestNormalizeDomainsSupportsSANAndWildcard(t *testing.T) {
|
|
got, err := NormalizeDomains([]string{"Example.COM.", "*.example.com", "example.com"})
|
|
if err != nil {
|
|
t.Fatalf("规范化域名失败:%v", err)
|
|
}
|
|
if len(got) != 2 || got[0] != "*.example.com" || got[1] != "example.com" {
|
|
t.Fatalf("规范化结果错误:%v", got)
|
|
}
|
|
}
|
|
|
|
func TestFileStoreSaveBacksUpAndKeepsKeyRestricted(t *testing.T) {
|
|
root := t.TempDir()
|
|
store := FileStore{Root: filepath.Join(root, "ssl"), BackupRoot: filepath.Join(root, "backups"), Now: func() time.Time { return time.Date(2026, 8, 26, 10, 0, 0, 0, time.UTC) }}
|
|
resource := testResource(t, "example.com")
|
|
first, err := store.Save(context.Background(), "example.com", resource)
|
|
if err != nil {
|
|
t.Fatalf("第一次保存失败:%v", err)
|
|
}
|
|
if first.BackupDirectory != "" {
|
|
t.Fatal("第一次保存不应生成备份")
|
|
}
|
|
resource.Certificate = append([]byte(nil), resource.Certificate...)
|
|
second, err := store.Save(context.Background(), "example.com", resource)
|
|
if err != nil {
|
|
t.Fatalf("第二次保存失败:%v", err)
|
|
}
|
|
if second.BackupDirectory == "" {
|
|
t.Fatal("替换已有证书时应生成备份")
|
|
}
|
|
if _, err := os.Stat(second.FullChainPath); err != nil {
|
|
t.Fatalf("完整证书链不存在:%v", err)
|
|
}
|
|
if info, err := os.Stat(second.PrivateKeyPath); err != nil {
|
|
t.Fatalf("私钥不存在:%v", err)
|
|
} else if runtime.GOOS != "windows" && info.Mode().Perm() != 0o600 {
|
|
t.Fatalf("私钥权限错误:%o", info.Mode().Perm())
|
|
}
|
|
}
|
|
|
|
func TestValidateResourceRejectsMismatchedKey(t *testing.T) {
|
|
resource := testResource(t, "example.com")
|
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
resource.PrivateKey = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
|
|
if _, err := ValidateResource(resource); err == nil {
|
|
t.Fatal("证书与私钥不匹配时应失败")
|
|
}
|
|
}
|
|
|
|
func TestValidateResourceForDomainsSupportsWildcard(t *testing.T) {
|
|
resource := testResourceForWildcard(t, "*.example.com")
|
|
if _, err := ValidateResourceForDomains(resource, []string{"*.example.com"}); err != nil {
|
|
t.Fatalf("泛域名证书覆盖校验失败:%v", err)
|
|
}
|
|
}
|
|
|
|
func testResource(t *testing.T, name string) Resource {
|
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
now := time.Now().UTC()
|
|
template := &x509.Certificate{SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: name}, DNSNames: []string{name}, NotBefore: now.Add(-time.Minute), NotAfter: now.Add(90 * 24 * time.Hour), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}}
|
|
der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return Resource{Domains: []string{name}, Certificate: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}), PrivateKey: pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})}
|
|
}
|
|
|
|
func testResourceForWildcard(t *testing.T, name string) Resource {
|
|
t.Helper()
|
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
now := time.Now().UTC()
|
|
template := &x509.Certificate{SerialNumber: big.NewInt(3), Subject: pkix.Name{CommonName: name}, DNSNames: []string{name}, NotBefore: now.Add(-time.Minute), NotAfter: now.Add(90 * 24 * time.Hour), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}}
|
|
der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return Resource{Domains: []string{name}, Certificate: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}), PrivateKey: pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})}
|
|
}
|