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