140 lines
4.3 KiB
Go
140 lines
4.3 KiB
Go
package crypto
|
||
|
||
import (
|
||
"crypto/ecdsa"
|
||
"crypto/elliptic"
|
||
"crypto/tls"
|
||
"crypto/x509"
|
||
"errors"
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"os"
|
||
"path/filepath"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
func TestLoadOrCreateServerCert_Creates(t *testing.T) {
|
||
dir := t.TempDir()
|
||
sc, err := LoadOrCreateServerCert(dir)
|
||
if err != nil {
|
||
t.Fatalf("LoadOrCreateServerCert: %v", err)
|
||
}
|
||
for name, wantPerm := range map[string]os.FileMode{serverKeyFile: 0o600, serverCertFile: 0o644} {
|
||
info, err := os.Stat(filepath.Join(dir, name))
|
||
if err != nil {
|
||
t.Fatalf("%s fehlt: %v", name, err)
|
||
}
|
||
if info.Mode().Perm() != wantPerm {
|
||
t.Errorf("%s hat %o, erwartet %o", name, info.Mode().Perm(), wantPerm)
|
||
}
|
||
}
|
||
|
||
leaf := sc.Leaf()
|
||
key, ok := leaf.PublicKey.(*ecdsa.PublicKey)
|
||
if !ok || key.Curve != elliptic.P256() {
|
||
t.Errorf("Schlüssel ist kein ECDSA P-256: %T", leaf.PublicKey)
|
||
}
|
||
// Selbstsigniert, aber bewusst keine CA (könnte sonst weitere
|
||
// Zertifikate ausstellen) – daher nicht CheckSignatureFrom.
|
||
if err := leaf.CheckSignature(leaf.SignatureAlgorithm, leaf.RawTBSCertificate, leaf.Signature); err != nil ||
|
||
leaf.Issuer.String() != leaf.Subject.String() {
|
||
t.Errorf("nicht selbstsigniert: %v", err)
|
||
}
|
||
if leaf.IsCA {
|
||
t.Error("Server-Zertifikat darf keine CA sein")
|
||
}
|
||
if len(leaf.ExtKeyUsage) != 1 || leaf.ExtKeyUsage[0] != x509.ExtKeyUsageServerAuth {
|
||
t.Errorf("ExtKeyUsage = %v, erwartet ServerAuth", leaf.ExtKeyUsage)
|
||
}
|
||
if leaf.NotAfter.Before(time.Now().AddDate(9, 0, 0)) {
|
||
t.Errorf("gültig nur bis %v – ein Ablauf würde alle Pins brechen", leaf.NotAfter)
|
||
}
|
||
if sc.Fingerprint != FingerprintOf(leaf) {
|
||
t.Error("Fingerprint passt nicht zum Zertifikat")
|
||
}
|
||
}
|
||
|
||
func TestLoadOrCreateServerCert_StableAcrossRestarts(t *testing.T) {
|
||
dir := t.TempDir()
|
||
first, err := LoadOrCreateServerCert(dir)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
second, err := LoadOrCreateServerCert(dir)
|
||
if err != nil {
|
||
t.Fatalf("erneutes Laden: %v", err)
|
||
}
|
||
if first.Fingerprint != second.Fingerprint {
|
||
t.Error("Fingerprint hat sich beim Neustart geändert – alle Agents würden die Verbindung verweigern")
|
||
}
|
||
}
|
||
|
||
func TestLoadOrCreateServerCert_Errors(t *testing.T) {
|
||
t.Run("nur Zertifikat vorhanden", func(t *testing.T) {
|
||
dir := t.TempDir()
|
||
LoadOrCreateServerCert(dir)
|
||
os.Remove(filepath.Join(dir, serverKeyFile))
|
||
if _, err := LoadOrCreateServerCert(dir); !errors.Is(err, ErrIncompleteCert) {
|
||
t.Errorf("erwartet ErrIncompleteCert, bekommen %v", err)
|
||
}
|
||
if _, err := os.Stat(filepath.Join(dir, serverKeyFile)); err == nil {
|
||
t.Error("fehlender Schlüssel wurde stillschweigend neu erzeugt")
|
||
}
|
||
})
|
||
t.Run("nur Schlüssel vorhanden", func(t *testing.T) {
|
||
dir := t.TempDir()
|
||
LoadOrCreateServerCert(dir)
|
||
os.Remove(filepath.Join(dir, serverCertFile))
|
||
if _, err := LoadOrCreateServerCert(dir); !errors.Is(err, ErrIncompleteCert) {
|
||
t.Errorf("erwartet ErrIncompleteCert, bekommen %v", err)
|
||
}
|
||
})
|
||
t.Run("Schlüssel zu offen", func(t *testing.T) {
|
||
dir := t.TempDir()
|
||
LoadOrCreateServerCert(dir)
|
||
os.Chmod(filepath.Join(dir, serverKeyFile), 0o644)
|
||
if _, err := LoadOrCreateServerCert(dir); !errors.Is(err, ErrInsecureKeyFile) {
|
||
t.Errorf("erwartet ErrInsecureKeyFile, bekommen %v", err)
|
||
}
|
||
})
|
||
t.Run("Schlüssel passt nicht zum Zertifikat", func(t *testing.T) {
|
||
a, b := t.TempDir(), t.TempDir()
|
||
LoadOrCreateServerCert(a)
|
||
LoadOrCreateServerCert(b)
|
||
data, _ := os.ReadFile(filepath.Join(b, serverCertFile))
|
||
os.WriteFile(filepath.Join(a, serverCertFile), data, 0o644)
|
||
if _, err := LoadOrCreateServerCert(a); err == nil {
|
||
t.Error("erwartet Fehler bei fremdem Zertifikat")
|
||
}
|
||
})
|
||
t.Run("Verzeichnis fehlt", func(t *testing.T) {
|
||
if _, err := LoadOrCreateServerCert(filepath.Join(t.TempDir(), "fehlt")); err == nil {
|
||
t.Error("erwartet Fehler")
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestServerTLSConfig_OnlyTLS13(t *testing.T) {
|
||
sc, err := LoadOrCreateServerCert(t.TempDir())
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
cfg := ServerTLSConfig(sc)
|
||
if cfg.MinVersion != tls.VersionTLS13 {
|
||
t.Errorf("MinVersion = %x, erwartet TLS 1.3", cfg.MinVersion)
|
||
}
|
||
|
||
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
|
||
srv.TLS = cfg
|
||
srv.StartTLS()
|
||
defer srv.Close()
|
||
|
||
old := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{
|
||
InsecureSkipVerify: true, MaxVersion: tls.VersionTLS12,
|
||
}}}
|
||
if _, err := old.Get(srv.URL); err == nil {
|
||
t.Error("TLS-1.2-Client wurde akzeptiert")
|
||
}
|
||
}
|