| // Copyright 2026 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package tls |
| |
| //go:generate go test -run ^TestGenerateCertificates$ crypto/tls -generate |
| |
| import ( |
| "bytes" |
| "crypto/ecdsa" |
| "crypto/ed25519" |
| "crypto/elliptic" |
| "crypto/fips140" |
| "crypto/rand" |
| "crypto/rsa" |
| "crypto/x509" |
| "crypto/x509/pkix" |
| "encoding/pem" |
| "flag" |
| "fmt" |
| "internal/testenv" |
| "math/big" |
| "os" |
| "strings" |
| "testing" |
| "testing/cryptotest" |
| "time" |
| ) |
| |
| var generate = flag.Bool("generate", false, "regenerate certificates_test.go") |
| |
| func TestGenerateCertificates(t *testing.T) { |
| testenv.MustHaveSource(t) |
| if testing.Short() && !*generate { |
| t.Skip("set -generate to regenerate certificates_test.go, or run without -short to check") |
| } |
| if fips140.Version() == "v1.0.0" { |
| t.Skip("FIPS 140-3 module v1.0.0 doesn't support SetGlobalRandom") |
| } |
| |
| // Allow RSA keys below 1024 bits for testRSA512. |
| t.Setenv("GODEBUG", os.Getenv("GODEBUG")+",rsa1024min=0") |
| // Unset cryptocustomrand to avoid MaybeReadByte non-determinism. |
| t.Setenv("GODEBUG", os.Getenv("GODEBUG")+",cryptocustomrand=0") |
| cryptotest.SetGlobalRandom(t, 0) |
| |
| notBefore := time.Unix(1476984729, 0).Add(-100 * 24 * time.Hour) |
| notAfter := time.Unix(1476984729, 0).Add(100 * 24 * time.Hour) |
| serial := int64(0) |
| nextSerial := func() *big.Int { |
| serial++ |
| return big.NewInt(serial) |
| } |
| |
| // Root CA key and cert. |
| rootKey, err := rsa.GenerateKey(rand.Reader, 2048) |
| if err != nil { |
| t.Fatal(err) |
| } |
| rootTemplate := &x509.Certificate{ |
| SerialNumber: nextSerial(), |
| Subject: pkix.Name{CommonName: "Root"}, |
| NotBefore: notBefore, |
| NotAfter: notAfter, |
| KeyUsage: x509.KeyUsageCertSign, |
| BasicConstraintsValid: true, |
| IsCA: true, |
| } |
| rootDER, err := x509.CreateCertificate(rand.Reader, rootTemplate, rootTemplate, &rootKey.PublicKey, rootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| rootCert, err := x509.ParseCertificate(rootDER) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // Client Root CA key and cert. |
| clientRootKey, err := rsa.GenerateKey(rand.Reader, 2048) |
| if err != nil { |
| t.Fatal(err) |
| } |
| clientRootTemplate := &x509.Certificate{ |
| SerialNumber: nextSerial(), |
| Subject: pkix.Name{CommonName: "Client Root"}, |
| NotBefore: notBefore, |
| NotAfter: notAfter, |
| KeyUsage: x509.KeyUsageCertSign, |
| BasicConstraintsValid: true, |
| IsCA: true, |
| } |
| clientRootDER, err := x509.CreateCertificate(rand.Reader, clientRootTemplate, clientRootTemplate, &clientRootKey.PublicKey, clientRootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| clientRootCert, err := x509.ParseCertificate(clientRootDER) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // Helper to create a leaf template. |
| serverLeaf := func(cn string, san string) *x509.Certificate { |
| return &x509.Certificate{ |
| SerialNumber: nextSerial(), |
| Subject: pkix.Name{CommonName: cn}, |
| NotBefore: notBefore, |
| NotAfter: notAfter, |
| KeyUsage: x509.KeyUsageDigitalSignature, |
| ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, |
| BasicConstraintsValid: true, |
| DNSNames: []string{san}, |
| } |
| } |
| clientLeaf := func(cn string, san string) *x509.Certificate { |
| return &x509.Certificate{ |
| SerialNumber: nextSerial(), |
| Subject: pkix.Name{CommonName: cn}, |
| NotBefore: notBefore, |
| NotAfter: notAfter, |
| KeyUsage: x509.KeyUsageDigitalSignature, |
| ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, |
| BasicConstraintsValid: true, |
| DNSNames: []string{san}, |
| } |
| } |
| |
| type certKeyPair struct { |
| name string |
| comment string |
| certPEM string |
| keyPEM string |
| } |
| var pairs []certKeyPair |
| |
| emit := func(name, comment string, certDER []byte, key any) { |
| keyDER, err := x509.MarshalPKCS8PrivateKey(key) |
| if err != nil { |
| t.Fatal(err) |
| } |
| certPEM := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})) |
| keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "TESTING KEY", Bytes: keyDER})) |
| pairs = append(pairs, certKeyPair{name, comment, strings.TrimSpace(certPEM), strings.TrimSpace(keyPEM)}) |
| } |
| |
| // Roots. |
| emit("testRoot", "Self-signed RSA 2048 root CA, CN=Root.", rootDER, rootKey) |
| emit("testClientRoot", "Self-signed RSA 2048 root CA, CN=Client Root.", clientRootDER, clientRootKey) |
| |
| // Server certs issued by root. |
| |
| // ECDSA P-256 (default). |
| ecdsaP256Key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl := serverLeaf("ECDSA P-256", "test.golang.example") |
| der, err := x509.CreateCertificate(rand.Reader, tmpl, rootCert, &ecdsaP256Key.PublicKey, rootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testECDSAP256", "ECDSA P-256 server leaf, SAN=test.golang.example, issued by Root.", der, ecdsaP256Key) |
| |
| // RSA 2048. |
| rsa2048Key, err := rsa.GenerateKey(rand.Reader, 2048) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = serverLeaf("RSA 2048", "test.golang.example") |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, rootCert, &rsa2048Key.PublicKey, rootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testRSA2048", "RSA 2048 server leaf, SAN=test.golang.example, issued by Root.", der, rsa2048Key) |
| |
| // ECDSA P-384. |
| ecdsaP384Key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = serverLeaf("ECDSA P-384", "test.golang.example") |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, rootCert, &ecdsaP384Key.PublicKey, rootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testECDSAP384", "ECDSA P-384 server leaf, SAN=test.golang.example, issued by Root.", der, ecdsaP384Key) |
| |
| // ECDSA P-521. |
| ecdsaP521Key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = serverLeaf("ECDSA P-521", "test.golang.example") |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, rootCert, &ecdsaP521Key.PublicKey, rootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testECDSAP521", "ECDSA P-521 server leaf, SAN=test.golang.example, issued by Root.", der, ecdsaP521Key) |
| |
| // Ed25519. |
| ed25519Pub, ed25519Key, err := ed25519.GenerateKey(rand.Reader) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = serverLeaf("Ed25519", "test.golang.example") |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, rootCert, ed25519Pub, rootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testEd25519", "Ed25519 server leaf, SAN=test.golang.example, issued by Root.", der, ed25519Key) |
| |
| // RSA-PSS: signed by root with SHA512WithRSAPSS. The leaf SPKI is |
| // rsaEncryption while the signatureAlgorithm is rsassaPss, for use |
| // with the rsa_pss_rsae_* SignatureSchemes. |
| rsaPSSKey, err := rsa.GenerateKey(rand.Reader, 2048) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = serverLeaf("RSA-PSS", "test.golang.example") |
| tmpl.SignatureAlgorithm = x509.SHA512WithRSAPSS |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, rootCert, &rsaPSSKey.PublicKey, rootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testRSAPSS", "RSA 2048 server leaf, SAN=test.golang.example, issued by Root.\n\t// Signature algorithm is SHA512WithRSAPSS (rsaEncryption SPKI, rsassaPss signature).", der, rsaPSSKey) |
| |
| // RSA 1024: key is intentionally too small for rsa_pss_rsae_sha512 |
| // (which requires at least 1040 bits), but large enough for |
| // rsa_pss_rsae_sha256. Used by TestHandshakeServerRSAPSS. |
| rsa1024Key, err := rsa.GenerateKey(rand.Reader, 1024) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = serverLeaf("RSA 1024", "test.golang.example") |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, rootCert, &rsa1024Key.PublicKey, rootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testRSA1024", "RSA 1024 server leaf, SAN=test.golang.example, issued by Root.\n\t// Key is too small for rsa_pss_rsae_sha512; used by TestHandshakeServerRSAPSS.", der, rsa1024Key) |
| |
| // RSA 512: key is too small for any rsa_pss_rsae_* SignatureScheme |
| // (the smallest, SHA-256, requires at least 528 bits). Used by |
| // TestKeyTooSmallForRSAPSS. |
| rsa512Key, err := rsa.GenerateKey(rand.Reader, 512) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = serverLeaf("RSA 512", "test.golang.example") |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, rootCert, &rsa512Key.PublicKey, rootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testRSA512", "RSA 512 server leaf, SAN=test.golang.example, issued by Root.\n\t// Key is too small for any rsa_pss_rsae_*; used by TestKeyTooSmallForRSAPSS.", der, rsa512Key) |
| |
| // SNI cert (different SAN for SNI mismatch testing). |
| sniKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = serverLeaf("different.example.com", "different.example.com") |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, rootCert, &sniKey.PublicKey, rootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testSNI", "ECDSA P-256 server leaf, SAN=different.example.com, issued by Root.", der, sniKey) |
| |
| // Client certs issued by client root. |
| |
| clientRSAKey, err := rsa.GenerateKey(rand.Reader, 2048) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = clientLeaf("clientAuth RSA 2048", "test.golang.example") |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, clientRootCert, &clientRSAKey.PublicKey, clientRootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testClientRSA2048", "RSA 2048 client leaf, SAN=test.golang.example, issued by Client Root.", der, clientRSAKey) |
| |
| clientECDSAKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = clientLeaf("clientAuth ECDSA P-256", "test.golang.example") |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, clientRootCert, &clientECDSAKey.PublicKey, clientRootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testClientECDSAP256", "ECDSA P-256 client leaf, SAN=test.golang.example, issued by Client Root.", der, clientECDSAKey) |
| |
| clientEd25519Pub, clientEd25519Key, err := ed25519.GenerateKey(rand.Reader) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = clientLeaf("clientAuth Ed25519", "test.golang.example") |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, clientRootCert, clientEd25519Pub, clientRootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testClientEd25519", "Ed25519 client leaf, SAN=test.golang.example, issued by Client Root.", der, clientEd25519Key) |
| |
| // Client RSA-PSS: signed by client root with SHA512WithRSAPSS. The leaf |
| // SPKI is rsaEncryption while the signatureAlgorithm is rsassaPss. |
| clientRSAPSSKey, err := rsa.GenerateKey(rand.Reader, 2048) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tmpl = clientLeaf("clientAuth RSA-PSS", "test.golang.example") |
| tmpl.SignatureAlgorithm = x509.SHA512WithRSAPSS |
| der, err = x509.CreateCertificate(rand.Reader, tmpl, clientRootCert, &clientRSAPSSKey.PublicKey, clientRootKey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| emit("testClientRSAPSS", "RSA 2048 client leaf, SAN=test.golang.example, issued by Client Root.\n\t// Signature algorithm is SHA512WithRSAPSS (rsaEncryption SPKI, rsassaPss signature).", der, clientRSAPSSKey) |
| |
| // Generate certificates_test.go. |
| var buf bytes.Buffer |
| fmt.Fprint(&buf, `// Code generated by certificates_generator_test.go; DO NOT EDIT. |
| // To regenerate, run: go generate |
| |
| package tls |
| |
| import "crypto/x509" |
| |
| `) |
| |
| fmt.Fprint(&buf, `var ( |
| `) |
| for _, p := range pairs { |
| fmt.Fprintf(&buf, "\t// %s\n", p.comment) |
| fmt.Fprintf(&buf, "\t%sCert = parseTestCert(%sCertPEM, %sKeyPEM)\n\n", |
| p.name, p.name, p.name) |
| } |
| fmt.Fprint(&buf, ` // x509.CertPool containing testRootCert. |
| testRootCertPool = newTestCertPool(testRootCertPEM) |
| // x509.CertPool containing testClientRootCert. |
| testClientRootCertPool = newTestCertPool(testClientRootCertPEM) |
| ) |
| |
| `) |
| |
| for _, p := range pairs { |
| fmt.Fprintf(&buf, "const %sCertPEM = `\n%s`\n\n", p.name, p.certPEM) |
| fmt.Fprintf(&buf, "const %sKeyPEM = `\n%s`\n\n", p.name, p.keyPEM) |
| } |
| |
| fmt.Fprint(&buf, `func parseTestCert(certPEM, keyPEM string) Certificate { |
| tlsCert, err := X509KeyPair([]byte(certPEM), []byte(testingKey(keyPEM))) |
| if err != nil { |
| panic(err) |
| } |
| return tlsCert |
| } |
| |
| func newTestCertPool(certPEM string) *x509.CertPool { |
| pool := x509.NewCertPool() |
| if !pool.AppendCertsFromPEM([]byte(certPEM)) { |
| panic("failed to parse certificate for pool") |
| } |
| return pool |
| } |
| `) |
| |
| if *generate { |
| if err := os.WriteFile("certificates_test.go", buf.Bytes(), 0644); err != nil { |
| t.Fatal(err) |
| } |
| t.Log("wrote certificates_test.go") |
| } else { |
| // Check that the generated content matches the existing file. |
| existing, err := os.ReadFile("certificates_test.go") |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !bytes.Equal(existing, buf.Bytes()) { |
| t.Fatal("certificates_test.go is out of date; run go generate to update it") |
| } |
| } |
| } |