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