launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"net"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	gc "launchpad.net/gocheck"
    20  
    21  	"launchpad.net/juju-core/cert"
    22  )
    23  
    24  func TestAll(t *testing.T) {
    25  	gc.TestingT(t)
    26  }
    27  
    28  type certSuite struct{}
    29  
    30  var _ = gc.Suite(certSuite{})
    31  
    32  func (certSuite) TestParseCertificate(c *gc.C) {
    33  	xcert, err := cert.ParseCert(caCertPEM)
    34  	c.Assert(err, gc.IsNil)
    35  	c.Assert(xcert.Subject.CommonName, gc.Equals, "juju testing")
    36  
    37  	xcert, err = cert.ParseCert(caKeyPEM)
    38  	c.Check(xcert, gc.IsNil)
    39  	c.Assert(err, gc.ErrorMatches, "no certificates found")
    40  
    41  	xcert, err = cert.ParseCert([]byte("hello"))
    42  	c.Check(xcert, gc.IsNil)
    43  	c.Assert(err, gc.ErrorMatches, "no certificates found")
    44  }
    45  
    46  func (certSuite) TestParseCertAndKey(c *gc.C) {
    47  	xcert, key, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM)
    48  	c.Assert(err, gc.IsNil)
    49  	c.Assert(xcert.Subject.CommonName, gc.Equals, "juju testing")
    50  	c.Assert(key, gc.NotNil)
    51  
    52  	c.Assert(xcert.PublicKey.(*rsa.PublicKey), gc.DeepEquals, &key.PublicKey)
    53  }
    54  
    55  func (certSuite) TestNewCA(c *gc.C) {
    56  	expiry := roundTime(time.Now().AddDate(0, 0, 1))
    57  	caCertPEM, caKeyPEM, err := cert.NewCA("foo", expiry)
    58  	c.Assert(err, gc.IsNil)
    59  
    60  	caCert, caKey, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM)
    61  	c.Assert(err, gc.IsNil)
    62  
    63  	c.Assert(caKey, gc.FitsTypeOf, (*rsa.PrivateKey)(nil))
    64  	c.Assert(caCert.Subject.CommonName, gc.Equals, `juju-generated CA for environment "foo"`)
    65  	c.Assert(caCert.NotAfter.Equal(expiry), gc.Equals, true)
    66  	c.Assert(caCert.BasicConstraintsValid, gc.Equals, true)
    67  	c.Assert(caCert.IsCA, gc.Equals, true)
    68  	//c.Assert(caCert.MaxPathLen, Equals, 0)	TODO it ends up as -1 - check that this is ok.
    69  }
    70  
    71  func (certSuite) TestNewServer(c *gc.C) {
    72  	expiry := roundTime(time.Now().AddDate(1, 0, 0))
    73  	caCertPEM, caKeyPEM, err := cert.NewCA("foo", expiry)
    74  	c.Assert(err, gc.IsNil)
    75  
    76  	caCert, _, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM)
    77  	c.Assert(err, gc.IsNil)
    78  
    79  	var noHostnames []string
    80  	srvCertPEM, srvKeyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, noHostnames)
    81  	c.Assert(err, gc.IsNil)
    82  
    83  	srvCert, srvKey, err := cert.ParseCertAndKey(srvCertPEM, srvKeyPEM)
    84  	c.Assert(err, gc.IsNil)
    85  	c.Assert(srvCert.Subject.CommonName, gc.Equals, "*")
    86  	c.Assert(srvCert.NotAfter.Equal(expiry), gc.Equals, true)
    87  	c.Assert(srvCert.BasicConstraintsValid, gc.Equals, false)
    88  	c.Assert(srvCert.IsCA, gc.Equals, false)
    89  
    90  	checkTLSConnection(c, caCert, srvCert, srvKey)
    91  }
    92  
    93  func (certSuite) TestNewServerHostnames(c *gc.C) {
    94  	type test struct {
    95  		hostnames           []string
    96  		expectedDNSNames    []string
    97  		expectedIPAddresses []net.IP
    98  	}
    99  	tests := []test{{
   100  		[]string{},
   101  		nil,
   102  		nil,
   103  	}, {
   104  		[]string{"example.com"},
   105  		[]string{"example.com"},
   106  		nil,
   107  	}, {
   108  		[]string{"example.com", "127.0.0.1"},
   109  		[]string{"example.com"},
   110  		[]net.IP{net.IPv4(127, 0, 0, 1).To4()},
   111  	}, {
   112  		[]string{"::1"},
   113  		nil,
   114  		[]net.IP{net.IPv6loopback},
   115  	}}
   116  	for i, t := range tests {
   117  		c.Logf("test %d: %v", i, t.hostnames)
   118  		expiry := roundTime(time.Now().AddDate(1, 0, 0))
   119  		srvCertPEM, srvKeyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, t.hostnames)
   120  		c.Assert(err, gc.IsNil)
   121  		srvCert, _, err := cert.ParseCertAndKey(srvCertPEM, srvKeyPEM)
   122  		c.Assert(err, gc.IsNil)
   123  		c.Assert(srvCert.DNSNames, gc.DeepEquals, t.expectedDNSNames)
   124  		c.Assert(srvCert.IPAddresses, gc.DeepEquals, t.expectedIPAddresses)
   125  	}
   126  }
   127  
   128  func (certSuite) TestWithNonUTCExpiry(c *gc.C) {
   129  	expiry, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", "2012-11-28 15:53:57 +0100 CET")
   130  	c.Assert(err, gc.IsNil)
   131  	certPEM, keyPEM, err := cert.NewCA("foo", expiry)
   132  	xcert, err := cert.ParseCert(certPEM)
   133  	c.Assert(err, gc.IsNil)
   134  	c.Assert(xcert.NotAfter.Equal(expiry), gc.Equals, true)
   135  
   136  	var noHostnames []string
   137  	certPEM, _, err = cert.NewServer(certPEM, keyPEM, expiry, noHostnames)
   138  	xcert, err = cert.ParseCert(certPEM)
   139  	c.Assert(err, gc.IsNil)
   140  	c.Assert(xcert.NotAfter.Equal(expiry), gc.Equals, true)
   141  }
   142  
   143  func (certSuite) TestNewServerWithInvalidCert(c *gc.C) {
   144  	var noHostnames []string
   145  	srvCert, srvKey, err := cert.NewServer(nonCACert, nonCAKey, time.Now(), noHostnames)
   146  	c.Check(srvCert, gc.IsNil)
   147  	c.Check(srvKey, gc.IsNil)
   148  	c.Assert(err, gc.ErrorMatches, "CA certificate is not a valid CA")
   149  }
   150  
   151  func (certSuite) TestVerify(c *gc.C) {
   152  	now := time.Now()
   153  	caCert, caKey, err := cert.NewCA("foo", now.Add(1*time.Minute))
   154  	c.Assert(err, gc.IsNil)
   155  
   156  	var noHostnames []string
   157  	srvCert, _, err := cert.NewServer(caCert, caKey, now.Add(3*time.Minute), noHostnames)
   158  	c.Assert(err, gc.IsNil)
   159  
   160  	err = cert.Verify(srvCert, caCert, now)
   161  	c.Assert(err, gc.IsNil)
   162  
   163  	err = cert.Verify(srvCert, caCert, now.Add(55*time.Second))
   164  	c.Assert(err, gc.IsNil)
   165  
   166  	// TODO(rog) why does this succeed?
   167  	// err = cert.Verify(srvCert, caCert, now.Add(-1 * time.Minute))
   168  	//c.Check(err, gc.ErrorMatches, "x509: certificate has expired or is not yet valid")
   169  
   170  	err = cert.Verify(srvCert, caCert, now.Add(2*time.Minute))
   171  	c.Check(err, gc.ErrorMatches, "x509: certificate has expired or is not yet valid")
   172  
   173  	caCert2, caKey2, err := cert.NewCA("bar", now.Add(1*time.Minute))
   174  	c.Assert(err, gc.IsNil)
   175  
   176  	// Check original server certificate against wrong CA.
   177  	err = cert.Verify(srvCert, caCert2, now)
   178  	c.Check(err, gc.ErrorMatches, "x509: certificate signed by unknown authority")
   179  
   180  	srvCert2, _, err := cert.NewServer(caCert2, caKey2, now.Add(1*time.Minute), noHostnames)
   181  	c.Assert(err, gc.IsNil)
   182  
   183  	// Check new server certificate against original CA.
   184  	err = cert.Verify(srvCert2, caCert, now)
   185  	c.Check(err, gc.ErrorMatches, "x509: certificate signed by unknown authority")
   186  }
   187  
   188  // checkTLSConnection checks that we can correctly perform a TLS
   189  // handshake using the given credentials.
   190  func checkTLSConnection(c *gc.C, caCert, srvCert *x509.Certificate, srvKey *rsa.PrivateKey) (caName string) {
   191  	clientCertPool := x509.NewCertPool()
   192  	clientCertPool.AddCert(caCert)
   193  
   194  	var inBytes, outBytes bytes.Buffer
   195  
   196  	const msg = "hello to the server"
   197  	p0, p1 := net.Pipe()
   198  	p0 = bufferedConn(p0, 3)
   199  	p0 = recordingConn(p0, &inBytes, &outBytes)
   200  
   201  	var clientState tls.ConnectionState
   202  	done := make(chan error)
   203  	go func() {
   204  		clientConn := tls.Client(p0, &tls.Config{
   205  			ServerName: "anyServer",
   206  			RootCAs:    clientCertPool,
   207  		})
   208  		defer clientConn.Close()
   209  
   210  		_, err := clientConn.Write([]byte(msg))
   211  		if err != nil {
   212  			done <- fmt.Errorf("client: %v", err)
   213  		}
   214  		clientState = clientConn.ConnectionState()
   215  		done <- nil
   216  	}()
   217  	go func() {
   218  		serverConn := tls.Server(p1, &tls.Config{
   219  			Certificates: []tls.Certificate{
   220  				newTLSCert(c, srvCert, srvKey),
   221  			},
   222  		})
   223  		defer serverConn.Close()
   224  		data, err := ioutil.ReadAll(serverConn)
   225  		if err != nil {
   226  			done <- fmt.Errorf("server: %v", err)
   227  			return
   228  		}
   229  		if string(data) != msg {
   230  			done <- fmt.Errorf("server: got %q; expected %q", data, msg)
   231  			return
   232  		}
   233  
   234  		done <- nil
   235  	}()
   236  
   237  	for i := 0; i < 2; i++ {
   238  		err := <-done
   239  		c.Check(err, gc.IsNil)
   240  	}
   241  
   242  	outData := string(outBytes.Bytes())
   243  	c.Assert(outData, gc.Not(gc.HasLen), 0)
   244  	if strings.Index(outData, msg) != -1 {
   245  		c.Fatalf("TLS connection not encrypted")
   246  	}
   247  	c.Assert(clientState.VerifiedChains, gc.HasLen, 1)
   248  	c.Assert(clientState.VerifiedChains[0], gc.HasLen, 2)
   249  	return clientState.VerifiedChains[0][1].Subject.CommonName
   250  }
   251  
   252  func newTLSCert(c *gc.C, cert *x509.Certificate, key *rsa.PrivateKey) tls.Certificate {
   253  	return tls.Certificate{
   254  		Certificate: [][]byte{cert.Raw},
   255  		PrivateKey:  key,
   256  	}
   257  }
   258  
   259  // bufferedConn adds buffering for at least
   260  // n writes to the given connection.
   261  func bufferedConn(c net.Conn, n int) net.Conn {
   262  	for i := 0; i < n; i++ {
   263  		p0, p1 := net.Pipe()
   264  		go copyClose(p1, c)
   265  		go copyClose(c, p1)
   266  		c = p0
   267  	}
   268  	return c
   269  }
   270  
   271  // recordingConn returns a connection which
   272  // records traffic in or out of the given connection.
   273  func recordingConn(c net.Conn, in, out io.Writer) net.Conn {
   274  	p0, p1 := net.Pipe()
   275  	go func() {
   276  		io.Copy(io.MultiWriter(c, out), p1)
   277  		c.Close()
   278  	}()
   279  	go func() {
   280  		io.Copy(io.MultiWriter(p1, in), c)
   281  		p1.Close()
   282  	}()
   283  	return p0
   284  }
   285  
   286  func copyClose(w io.WriteCloser, r io.Reader) {
   287  	io.Copy(w, r)
   288  	w.Close()
   289  }
   290  
   291  // roundTime returns t rounded to the previous whole second.
   292  func roundTime(t time.Time) time.Time {
   293  	return t.Add(time.Duration(-t.Nanosecond()))
   294  }
   295  
   296  var (
   297  	caCertPEM = []byte(`
   298  -----BEGIN CERTIFICATE-----
   299  MIIBnTCCAUmgAwIBAgIBADALBgkqhkiG9w0BAQUwJjENMAsGA1UEChMEanVqdTEV
   300  MBMGA1UEAxMManVqdSB0ZXN0aW5nMB4XDTEyMTExNDE0Mzg1NFoXDTIyMTExNDE0
   301  NDM1NFowJjENMAsGA1UEChMEanVqdTEVMBMGA1UEAxMManVqdSB0ZXN0aW5nMFow
   302  CwYJKoZIhvcNAQEBA0sAMEgCQQCCOOpn9aWKcKr2GQGtygwD7PdfNe1I9BYiPAqa
   303  2I33F5+6PqFdfujUKvoyTJI6XG4Qo/CECaaN9smhyq9DxzMhAgMBAAGjZjBkMA4G
   304  A1UdDwEB/wQEAwIABDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBQQDswP
   305  FQGeGMeTzPbHW62EZbbTJzAfBgNVHSMEGDAWgBQQDswPFQGeGMeTzPbHW62EZbbT
   306  JzALBgkqhkiG9w0BAQUDQQAqZzN0DqUyEfR8zIanozyD2pp10m9le+ODaKZDDNfH
   307  8cB2x26F1iZ8ccq5IC2LtQf1IKJnpTcYlLuDvW6yB96g
   308  -----END CERTIFICATE-----
   309  `)
   310  
   311  	caKeyPEM = []byte(`
   312  -----BEGIN RSA PRIVATE KEY-----
   313  MIIBOwIBAAJBAII46mf1pYpwqvYZAa3KDAPs91817Uj0FiI8CprYjfcXn7o+oV1+
   314  6NQq+jJMkjpcbhCj8IQJpo32yaHKr0PHMyECAwEAAQJAYctedh4raLE+Ir0a3qnK
   315  pjQSfiUggtYTvTf7+tfAnZu946PX88ysr7XHPkXEGP4tWDTbl8BfGndrTKswVOx6
   316  RQIhAOT5OzafJneDQ5cuGLN/hxIPBLWxKT1/25O6dhtBlRyPAiEAkZfFvCtBZyKB
   317  JFwDdp+7gE98mXtaFrjctLWeFx797U8CIAnnqiMTwWM8H2ljyhfBtYMXeTmu3zzU
   318  0hfS4hcNwDiLAiEAkNXXU7YEPkFJD46ps1x7/s0UOutHV8tXZD44ou+l1GkCIQDO
   319  HOzuvYngJpoClGw0ipzJPoNZ2Z/GkdOWGByPeKu/8g==
   320  -----END RSA PRIVATE KEY-----
   321  `)
   322  
   323  	nonCACert = []byte(`-----BEGIN CERTIFICATE-----
   324  MIIBmjCCAUagAwIBAgIBADALBgkqhkiG9w0BAQUwJjENMAsGA1UEChMEanVqdTEV
   325  MBMGA1UEAxMManVqdSB0ZXN0aW5nMB4XDTEyMTExNDE3MTU1NloXDTIyMTExNDE3
   326  MjA1NlowJjENMAsGA1UEChMEanVqdTEVMBMGA1UEAxMManVqdSB0ZXN0aW5nMFow
   327  CwYJKoZIhvcNAQEBA0sAMEgCQQC96/CsTTY1Va8et6QYNXwrssAi36asFlV/fksG
   328  hqRucidiz/+xHvhs9EiqEu7NGxeVAkcfIhXu6/BDlobtj2v5AgMBAAGjYzBhMA4G
   329  A1UdDwEB/wQEAwIABDAPBgNVHRMBAf8EBTADAgEBMB0GA1UdDgQWBBRqbxkIW4R0
   330  vmmkUoYuWg9sDob4jzAfBgNVHSMEGDAWgBRqbxkIW4R0vmmkUoYuWg9sDob4jzAL
   331  BgkqhkiG9w0BAQUDQQC3+KN8RppKdvlbP6fDwRC22PaCxd0PVyIHsn7I4jgpBPf8
   332  Z3codMYYA5/f0AmUsD7wi7nnJVPPLZK7JWu4VI/w
   333  -----END CERTIFICATE-----
   334  `)
   335  
   336  	nonCAKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
   337  MIIBOgIBAAJBAL3r8KxNNjVVrx63pBg1fCuywCLfpqwWVX9+SwaGpG5yJ2LP/7Ee
   338  +Gz0SKoS7s0bF5UCRx8iFe7r8EOWhu2Pa/kCAwEAAQJAdzuAxStUNPeuEWLJKkmp
   339  wuVdqocuZCtBUeE/yMEOyibZ9NLKSuDJuDorkoeoiBz2vyUITHkLp4jgNmCI8NGg
   340  AQIhAPZG9+3OghlzcqWR4nTho8KO/CuO9bu5G4jNEdIrSJ6BAiEAxWtoLZNMwI4Q
   341  kj2moFk9GdBXZV9I0t1VTwcDvVyeAXkCIDrfvldQPdO9wJOKK3vLkS1qpyf2lhIZ
   342  b1alx3PZuxOBAiAthPltYMRWtar+fTaZTFo5RH+SQSkibaRI534mQF+ySQIhAIml
   343  yiWVLC2XrtwijDu1fwh/wtFCb/bPvqvgG5wgAO+2
   344  -----END RSA PRIVATE KEY-----
   345  `)
   346  )