certgen_test.go raw

   1  package util_test
   2  
   3  import (
   4  	"crypto/x509"
   5  	"encoding/pem"
   6  	"net"
   7  	"testing"
   8  	"time"
   9  	
  10  	"github.com/p9c/p9/pkg/util"
  11  	// "github.com/davecgh/go-spew/spew"
  12  )
  13  
  14  // TestNewTLSCertPair ensures the NewTLSCertPair function works as expected.
  15  func TestNewTLSCertPair(t *testing.T) {
  16  	// Certs don't support sub-second precision, so truncate it now to ensure the checks later don't fail due to
  17  	// nanosecond precision differences.
  18  	validUntil := time.Unix(time.Now().Add(10*365*24*time.Hour).Unix(), 0)
  19  	org := "test autogenerated cert"
  20  	extraHosts := []string{"testtlscert.bogus", "localhost", "127.0.0.1"}
  21  	cert, key, e := util.NewTLSCertPair(org, validUntil, extraHosts)
  22  	if e != nil {
  23  		t.Fatalf("failed with unexpected error: %v", e)
  24  	}
  25  	// Ensure the PEM-encoded cert that is returned can be decoded.
  26  	pemCert, _ := pem.Decode(cert)
  27  	if pemCert == nil {
  28  		t.Fatalf("pem.Decode was unable to decode the certificate")
  29  	}
  30  	// Ensure the PEM-encoded key that is returned can be decoded.
  31  	pemKey, _ := pem.Decode(key)
  32  	if pemKey == nil {
  33  		t.Fatalf("pem.Decode was unable to decode the key")
  34  	}
  35  	// Ensure the DER-encoded key bytes can be successfully parsed.
  36  	_, e = x509.ParseECPrivateKey(pemKey.Bytes)
  37  	if e != nil {
  38  		t.Fatalf("failed with unexpected error: %v", e)
  39  	}
  40  	// Ensure the DER-encoded cert bytes can be successfully into an X.509 certificate.
  41  	x509Cert, e := x509.ParseCertificate(pemCert.Bytes)
  42  	if e != nil {
  43  		t.Fatalf("failed with unexpected error: %v", e)
  44  	}
  45  	// Ensure the specified organization is correct.
  46  	x509Orgs := x509Cert.Subject.Organization
  47  	if len(x509Orgs) == 0 || x509Orgs[0] != org {
  48  		x509Org := "<no organization>"
  49  		if len(x509Orgs) > 0 {
  50  			x509Org = x509Orgs[0]
  51  		}
  52  		t.Fatalf("generated cert organization field mismatch, got "+
  53  			"'%v', want '%v'", x509Org, org,
  54  		)
  55  	}
  56  	// Ensure the specified valid until value is correct.
  57  	if !x509Cert.NotAfter.Equal(validUntil) {
  58  		t.Fatalf("generated cert valid until field mismatch, got %v, "+
  59  			"want %v", x509Cert.NotAfter, validUntil,
  60  		)
  61  	}
  62  	// Ensure the specified extra hosts are present.
  63  	for _, host := range extraHosts {
  64  		if e := x509Cert.VerifyHostname(host); E.Chk(e) {
  65  			t.Fatalf("failed to verify extra host '%s'", host)
  66  		}
  67  	}
  68  	// Ensure that the Common Name is also the first SAN DNS name.
  69  	cn := x509Cert.Subject.CommonName
  70  	san0 := x509Cert.DNSNames[0]
  71  	if cn != san0 {
  72  		t.Errorf("common name %s does not match first SAN %s", cn, san0)
  73  	}
  74  	// Ensure there are no duplicate hosts or IPs.
  75  	hostCounts := make(map[string]int)
  76  	for _, host := range x509Cert.DNSNames {
  77  		hostCounts[host]++
  78  	}
  79  	ipCounts := make(map[string]int)
  80  	for _, ip := range x509Cert.IPAddresses {
  81  		ipCounts[string(ip)]++
  82  	}
  83  	for host, count := range hostCounts {
  84  		if count != 1 {
  85  			t.Errorf("host %s appears %d times in certificate", host, count)
  86  		}
  87  	}
  88  	for ipStr, count := range ipCounts {
  89  		if count != 1 {
  90  			t.Errorf("ip %s appears %d times in certificate", net.IP(ipStr), count)
  91  		}
  92  	}
  93  	// Ensure the cert can be use for the intended purposes.
  94  	if !x509Cert.IsCA {
  95  		t.Fatal("generated cert is not a certificate authority")
  96  	}
  97  	if x509Cert.KeyUsage&x509.KeyUsageKeyEncipherment == 0 {
  98  		t.Fatal("generated cert can't be used for key encipherment")
  99  	}
 100  	if x509Cert.KeyUsage&x509.KeyUsageDigitalSignature == 0 {
 101  		t.Fatal("generated cert can't be used for digital signatures")
 102  	}
 103  	if x509Cert.KeyUsage&x509.KeyUsageCertSign == 0 {
 104  		t.Fatal("generated cert can't be used for signing other certs")
 105  	}
 106  	if !x509Cert.BasicConstraintsValid {
 107  		t.Fatal("generated cert does not have valid basic constraints")
 108  	}
 109  }
 110