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