github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cert/cert_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cert_test
     5  
     6  import (
     7  	"bytes"
     8  	"crypto/rsa"
     9  	"crypto/tls"
    10  	"crypto/x509"
    11  	"io"
    12  	"io/ioutil"
    13  	"math/big"
    14  	"net"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	jc "github.com/juju/testing/checkers"
    20  	"github.com/juju/utils"
    21  	gc "gopkg.in/check.v1"
    22  
    23  	"github.com/juju/juju/cert"
    24  )
    25  
    26  func TestAll(t *testing.T) {
    27  	gc.TestingT(t)
    28  }
    29  
    30  type certSuite struct{}
    31  
    32  var _ = gc.Suite(certSuite{})
    33  
    34  func checkNotBefore(c *gc.C, cert *x509.Certificate, now time.Time) {
    35  	// Check that the certificate is valid from one week before today.
    36  	c.Check(cert.NotBefore.Before(now), jc.IsTrue)
    37  	c.Check(cert.NotBefore.Before(now.AddDate(0, 0, -6)), jc.IsTrue)
    38  	c.Check(cert.NotBefore.After(now.AddDate(0, 0, -8)), jc.IsTrue)
    39  }
    40  
    41  func checkNotAfter(c *gc.C, cert *x509.Certificate, expiry time.Time) {
    42  	// Check the surrounding day.
    43  	c.Assert(cert.NotAfter.Before(expiry.AddDate(0, 0, 1)), jc.IsTrue)
    44  	c.Assert(cert.NotAfter.After(expiry.AddDate(0, 0, -1)), jc.IsTrue)
    45  }
    46  
    47  func (certSuite) TestParseCertificate(c *gc.C) {
    48  	xcert, err := cert.ParseCert(caCertPEM)
    49  	c.Assert(err, jc.ErrorIsNil)
    50  	c.Assert(xcert.Subject.CommonName, gc.Equals, `juju-generated CA for model "juju testing"`)
    51  
    52  	xcert, err = cert.ParseCert(caKeyPEM)
    53  	c.Check(xcert, gc.IsNil)
    54  	c.Assert(err, gc.ErrorMatches, "no certificates found")
    55  
    56  	xcert, err = cert.ParseCert("hello")
    57  	c.Check(xcert, gc.IsNil)
    58  	c.Assert(err, gc.ErrorMatches, "no certificates found")
    59  }
    60  
    61  func (certSuite) TestParseCertAndKey(c *gc.C) {
    62  	xcert, key, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM)
    63  	c.Assert(err, jc.ErrorIsNil)
    64  	c.Assert(xcert.Subject.CommonName, gc.Equals, `juju-generated CA for model "juju testing"`)
    65  	c.Assert(key, gc.NotNil)
    66  
    67  	c.Assert(xcert.PublicKey.(*rsa.PublicKey), gc.DeepEquals, &key.PublicKey)
    68  }
    69  
    70  func (certSuite) TestNewCA(c *gc.C) {
    71  	now := time.Now()
    72  	expiry := roundTime(now.AddDate(0, 0, 1))
    73  	caCertPEM, caKeyPEM, err := cert.NewCA("foo", "1", expiry)
    74  	c.Assert(err, jc.ErrorIsNil)
    75  
    76  	caCert, caKey, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM)
    77  	c.Assert(err, jc.ErrorIsNil)
    78  
    79  	c.Check(caKey, gc.FitsTypeOf, (*rsa.PrivateKey)(nil))
    80  	c.Check(caCert.Subject.CommonName, gc.Equals, `juju-generated CA for model "foo"`)
    81  	checkNotBefore(c, caCert, now)
    82  	checkNotAfter(c, caCert, expiry)
    83  	c.Check(caCert.BasicConstraintsValid, jc.IsTrue)
    84  	c.Check(caCert.IsCA, jc.IsTrue)
    85  	//c.Assert(caCert.MaxPathLen, Equals, 0)	TODO it ends up as -1 - check that this is ok.
    86  }
    87  
    88  func (certSuite) TestNewServer(c *gc.C) {
    89  	now := time.Now()
    90  	expiry := roundTime(now.AddDate(1, 0, 0))
    91  	caCertPEM, caKeyPEM, err := cert.NewCA("foo", "1", expiry)
    92  	c.Assert(err, jc.ErrorIsNil)
    93  
    94  	caCert, _, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM)
    95  	c.Assert(err, jc.ErrorIsNil)
    96  
    97  	srvCertPEM, srvKeyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, nil)
    98  	c.Assert(err, jc.ErrorIsNil)
    99  	checkCertificate(c, caCert, srvCertPEM, srvKeyPEM, now, expiry)
   100  }
   101  
   102  func (certSuite) TestNewDefaultServer(c *gc.C) {
   103  	now := time.Now()
   104  	expiry := roundTime(now.AddDate(1, 0, 0))
   105  	caCertPEM, caKeyPEM, err := cert.NewCA("foo", "1", expiry)
   106  	c.Assert(err, jc.ErrorIsNil)
   107  
   108  	caCert, _, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM)
   109  	c.Assert(err, jc.ErrorIsNil)
   110  
   111  	srvCertPEM, srvKeyPEM, err := cert.NewDefaultServer(caCertPEM, caKeyPEM, nil)
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	srvCertExpiry := roundTime(time.Now().AddDate(10, 0, 0))
   114  	checkCertificate(c, caCert, srvCertPEM, srvKeyPEM, now, srvCertExpiry)
   115  }
   116  
   117  func checkCertificate(c *gc.C, caCert *x509.Certificate, srvCertPEM, srvKeyPEM string, now, expiry time.Time) {
   118  	srvCert, srvKey, err := cert.ParseCertAndKey(srvCertPEM, srvKeyPEM)
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	c.Assert(srvCert.Subject.CommonName, gc.Equals, "*")
   121  	checkNotBefore(c, srvCert, now)
   122  	checkNotAfter(c, srvCert, expiry)
   123  	c.Assert(srvCert.BasicConstraintsValid, jc.IsFalse)
   124  	c.Assert(srvCert.IsCA, jc.IsFalse)
   125  	c.Assert(srvCert.ExtKeyUsage, gc.DeepEquals, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth})
   126  	c.Assert(srvCert.SerialNumber, gc.NotNil)
   127  	if srvCert.SerialNumber.Cmp(big.NewInt(0)) == 0 {
   128  		c.Fatalf("zero serial number")
   129  	}
   130  
   131  	checkTLSConnection(c, caCert, srvCert, srvKey)
   132  }
   133  
   134  func (certSuite) TestNewServerHostnames(c *gc.C) {
   135  	type test struct {
   136  		hostnames           []string
   137  		expectedDNSNames    []string
   138  		expectedIPAddresses []net.IP
   139  	}
   140  	tests := []test{{
   141  		[]string{},
   142  		nil,
   143  		nil,
   144  	}, {
   145  		[]string{"example.com"},
   146  		[]string{"example.com"},
   147  		nil,
   148  	}, {
   149  		[]string{"example.com", "127.0.0.1"},
   150  		[]string{"example.com"},
   151  		[]net.IP{net.IPv4(127, 0, 0, 1).To4()},
   152  	}, {
   153  		[]string{"::1"},
   154  		nil,
   155  		[]net.IP{net.IPv6loopback},
   156  	}}
   157  	for i, t := range tests {
   158  		c.Logf("test %d: %v", i, t.hostnames)
   159  		expiry := roundTime(time.Now().AddDate(1, 0, 0))
   160  		srvCertPEM, srvKeyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, t.hostnames)
   161  		c.Assert(err, jc.ErrorIsNil)
   162  		srvCert, _, err := cert.ParseCertAndKey(srvCertPEM, srvKeyPEM)
   163  		c.Assert(err, jc.ErrorIsNil)
   164  		c.Assert(srvCert.DNSNames, gc.DeepEquals, t.expectedDNSNames)
   165  		c.Assert(srvCert.IPAddresses, gc.DeepEquals, t.expectedIPAddresses)
   166  	}
   167  }
   168  
   169  func (certSuite) TestWithNonUTCExpiry(c *gc.C) {
   170  	expiry, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", "2012-11-28 15:53:57 +0100 CET")
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	certPEM, keyPEM, err := cert.NewCA("foo", "1", expiry)
   173  	xcert, err := cert.ParseCert(certPEM)
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	checkNotAfter(c, xcert, expiry)
   176  
   177  	var noHostnames []string
   178  	certPEM, _, err = cert.NewServer(certPEM, keyPEM, expiry, noHostnames)
   179  	xcert, err = cert.ParseCert(certPEM)
   180  	c.Assert(err, jc.ErrorIsNil)
   181  	checkNotAfter(c, xcert, expiry)
   182  }
   183  
   184  func (certSuite) TestNewServerWithInvalidCert(c *gc.C) {
   185  	var noHostnames []string
   186  	srvCert, srvKey, err := cert.NewServer(nonCACert, nonCAKey, time.Now(), noHostnames)
   187  	c.Check(srvCert, gc.Equals, "")
   188  	c.Check(srvKey, gc.Equals, "")
   189  	c.Assert(err, gc.ErrorMatches, "CA certificate is not a valid CA")
   190  }
   191  
   192  func (certSuite) TestVerify(c *gc.C) {
   193  	now := time.Now()
   194  	caCert, caKey, err := cert.NewCA("foo", "1", now.Add(1*time.Minute))
   195  	c.Assert(err, jc.ErrorIsNil)
   196  
   197  	var noHostnames []string
   198  	srvCert, _, err := cert.NewServer(caCert, caKey, now.Add(3*time.Minute), noHostnames)
   199  	c.Assert(err, jc.ErrorIsNil)
   200  
   201  	err = cert.Verify(srvCert, caCert, now)
   202  	c.Assert(err, jc.ErrorIsNil)
   203  
   204  	err = cert.Verify(srvCert, caCert, now.Add(55*time.Second))
   205  	c.Assert(err, jc.ErrorIsNil)
   206  
   207  	err = cert.Verify(srvCert, caCert, now.AddDate(0, 0, -8))
   208  	c.Check(err, gc.ErrorMatches, "x509: certificate has expired or is not yet valid")
   209  
   210  	err = cert.Verify(srvCert, caCert, now.Add(2*time.Minute))
   211  	c.Check(err, gc.ErrorMatches, "x509: certificate has expired or is not yet valid")
   212  
   213  	caCert2, caKey2, err := cert.NewCA("bar", "1", now.Add(1*time.Minute))
   214  	c.Assert(err, jc.ErrorIsNil)
   215  
   216  	// Check original server certificate against wrong CA.
   217  	err = cert.Verify(srvCert, caCert2, now)
   218  	c.Check(err, gc.ErrorMatches, "x509: certificate signed by unknown authority")
   219  
   220  	srvCert2, _, err := cert.NewServer(caCert2, caKey2, now.Add(1*time.Minute), noHostnames)
   221  	c.Assert(err, jc.ErrorIsNil)
   222  
   223  	// Check new server certificate against original CA.
   224  	err = cert.Verify(srvCert2, caCert, now)
   225  	c.Check(err, gc.ErrorMatches, "x509: certificate signed by unknown authority")
   226  }
   227  
   228  // checkTLSConnection checks that we can correctly perform a TLS
   229  // handshake using the given credentials.
   230  func checkTLSConnection(c *gc.C, caCert, srvCert *x509.Certificate, srvKey *rsa.PrivateKey) (caName string) {
   231  	clientCertPool := x509.NewCertPool()
   232  	clientCertPool.AddCert(caCert)
   233  
   234  	var outBytes bytes.Buffer
   235  
   236  	const msg = "hello to the server"
   237  	p0, p1 := net.Pipe()
   238  	p0 = &recordingConn{
   239  		Conn:   p0,
   240  		Writer: io.MultiWriter(p0, &outBytes),
   241  	}
   242  
   243  	var clientState tls.ConnectionState
   244  	done := make(chan error)
   245  	go func() {
   246  		config := utils.SecureTLSConfig()
   247  		config.Certificates = []tls.Certificate{{
   248  			Certificate: [][]byte{srvCert.Raw},
   249  			PrivateKey:  srvKey,
   250  		}}
   251  
   252  		conn := tls.Server(p1, config)
   253  		defer conn.Close()
   254  		data, err := ioutil.ReadAll(conn)
   255  		c.Assert(err, jc.ErrorIsNil)
   256  		c.Assert(string(data), gc.Equals, msg)
   257  		close(done)
   258  	}()
   259  
   260  	tlsConfig := utils.SecureTLSConfig()
   261  	tlsConfig.ServerName = "anyServer"
   262  	tlsConfig.RootCAs = clientCertPool
   263  	clientConn := tls.Client(p0, tlsConfig)
   264  	defer clientConn.Close()
   265  
   266  	_, err := clientConn.Write([]byte(msg))
   267  	c.Assert(err, jc.ErrorIsNil)
   268  	clientState = clientConn.ConnectionState()
   269  	clientConn.Close()
   270  
   271  	// wait for server to exit
   272  	<-done
   273  
   274  	outData := outBytes.String()
   275  	c.Assert(outData, gc.Not(gc.HasLen), 0)
   276  	if strings.Index(outData, msg) != -1 {
   277  		c.Fatalf("TLS connection not encrypted")
   278  	}
   279  	c.Assert(clientState.VerifiedChains, gc.HasLen, 1)
   280  	c.Assert(clientState.VerifiedChains[0], gc.HasLen, 2)
   281  	return clientState.VerifiedChains[0][1].Subject.CommonName
   282  }
   283  
   284  type recordingConn struct {
   285  	net.Conn
   286  	io.Writer
   287  }
   288  
   289  func (c recordingConn) Write(buf []byte) (int, error) {
   290  	return c.Writer.Write(buf)
   291  }
   292  
   293  // roundTime returns t rounded to the previous whole second.
   294  func roundTime(t time.Time) time.Time {
   295  	return t.Add(time.Duration(-t.Nanosecond()))
   296  }
   297  
   298  var (
   299  	caCertPEM = `
   300  -----BEGIN CERTIFICATE-----
   301  MIICHDCCAcagAwIBAgIUfzWn5ktGMxD6OiTgfiZyvKdM+ZYwDQYJKoZIhvcNAQEL
   302  BQAwazENMAsGA1UEChMEanVqdTEzMDEGA1UEAwwqanVqdS1nZW5lcmF0ZWQgQ0Eg
   303  Zm9yIG1vZGVsICJqdWp1IHRlc3RpbmciMSUwIwYDVQQFExwxMjM0LUFCQ0QtSVMt
   304  Tk9ULUEtUkVBTC1VVUlEMB4XDTE2MDkyMTEwNDgyN1oXDTI2MDkyODEwNDgyN1ow
   305  azENMAsGA1UEChMEanVqdTEzMDEGA1UEAwwqanVqdS1nZW5lcmF0ZWQgQ0EgZm9y
   306  IG1vZGVsICJqdWp1IHRlc3RpbmciMSUwIwYDVQQFExwxMjM0LUFCQ0QtSVMtTk9U
   307  LUEtUkVBTC1VVUlEMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+0X+1zl2vt1wI4
   308  1Q+RnlltJyaJmtwCbHRhREXVGU7t0kTMMNERxqLnuNUyWRz90Rg8s9XvOtCqNYW7
   309  mypGrFECAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMBAf8w
   310  HQYDVR0OBBYEFHueMLZ1QJ/2sKiPIJ28TzjIMRENMA0GCSqGSIb3DQEBCwUAA0EA
   311  ovZN0RbUHrO8q9Eazh0qPO4mwW9jbGTDz126uNrLoz1g3TyWxIas1wRJ8IbCgxLy
   312  XUrBZO5UPZab66lJWXyseA==
   313  -----END CERTIFICATE-----
   314  `
   315  
   316  	caKeyPEM = `
   317  -----BEGIN RSA PRIVATE KEY-----
   318  MIIBOgIBAAJBAL+0X+1zl2vt1wI41Q+RnlltJyaJmtwCbHRhREXVGU7t0kTMMNER
   319  xqLnuNUyWRz90Rg8s9XvOtCqNYW7mypGrFECAwEAAQJAMPa+JaUHgO6foxam/LIB
   320  0u95N3OgFR+dWeBaEsgKDclpREdJ0rXNI+3C3kwqeEZR4omoPlBeSEewSkwHxpmI
   321  0QIhAOjKiHZ5v6R8haleipbDzkGUnZW07hEwL5Ld4MNx/QQ1AiEA0tEzSSNAdM0C
   322  M/vY0x5mekIYai8/tFSEG9PJ3ZkpEy0CIQCo9B3YxwI1Un777vbs903iQQeiWP+U
   323  EAHnOQvhLgDxpQIgGkpml+9igW5zoOH+h02aQBLwEoXz7tw/YW0HFrCcE70CIGkS
   324  ve4WjiEqnQaHNAPy0hY/1DfIgBOSpOfnkFHOk9vX
   325  -----END RSA PRIVATE KEY-----
   326  `
   327  
   328  	nonCACert = `
   329  -----BEGIN CERTIFICATE-----
   330  MIIB8jCCAZygAwIBAgIVANueMZWTFEIx6AcNAWsG4VL4sUn5MA0GCSqGSIb3DQEB
   331  CwUAMGsxDTALBgNVBAoTBGp1anUxMzAxBgNVBAMMKmp1anUtZ2VuZXJhdGVkIENB
   332  IGZvciBtb2RlbCAianVqdSB0ZXN0aW5nIjElMCMGA1UEBRMcMTIzNC1BQkNELUlT
   333  LU5PVC1BLVJFQUwtVVVJRDAeFw0xNjA5MjExMDQ4MjdaFw0yNjA5MjgxMDQ4Mjda
   334  MBsxDTALBgNVBAoTBGp1anUxCjAIBgNVBAMTASowXDANBgkqhkiG9w0BAQEFAANL
   335  ADBIAkEAwZps3qpPu2FCAhbxolf/BvSa+dMal3AhPMe+lwTuSbtS81W+WSrbwUSI
   336  ZKSGHYDpFRN6ytNjt1oPbDNKDIR30wIDAQABo2cwZTAOBgNVHQ8BAf8EBAMCA6gw
   337  EwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYEFNNUDrcyP/4RbGBpKeC3gmfL
   338  kjlwMB8GA1UdIwQYMBaAFHueMLZ1QJ/2sKiPIJ28TzjIMRENMA0GCSqGSIb3DQEB
   339  CwUAA0EALiurKx//Qh5TQQ0TmT0P5f7OFLIs5XPSS98Lseb92h12CPNO4kB000Yh
   340  Xa7kZRGngwFbvjzqZ0eOfmo0l8M23A==
   341  -----END CERTIFICATE-----
   342  `
   343  
   344  	nonCAKey = `
   345  -----BEGIN RSA PRIVATE KEY-----
   346  MIIBOwIBAAJBAMGabN6qT7thQgIW8aJX/wb0mvnTGpdwITzHvpcE7km7UvNVvlkq
   347  28FEiGSkhh2A6RUTesrTY7daD2wzSgyEd9MCAwEAAQJBAKfeuOvRjVUSneOl9Vsp
   348  Je7oBcD9dR8+kPNc1zungN7YVhIuxqvzXJSPeMGsHloPI+BcFFXv3t+eVCDT9sPL
   349  L+ECIQDq1nqVIEX3k5nn6eI0L5CQbIfEyvWGJ/mOGSo9TWdN+QIhANMMsopPb9ct
   350  Z61LqPmTtNX4nhHyMEjxbUzqzsZzsRcrAiBeYyhP6fHVSXERopK1kOyU79o+Aalf
   351  a4/FSl4M16CO2QIgOBQZpNKyvxRbhhqijZ6H4IstRUt7NQahqlyCEQ1Qsv0CIQDQ
   352  tUzgFwUpd6NVButkqWGqnmBeKUOs97dqSyOzN9Nk8w==
   353  -----END RSA PRIVATE KEY-----
   354  `
   355  )