github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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 "github.com/juju/utils" 20 gc "gopkg.in/check.v1" 21 22 "github.com/juju/juju/cert" 23 ) 24 25 func TestAll(t *testing.T) { 26 gc.TestingT(t) 27 } 28 29 type certSuite struct{} 30 31 var _ = gc.Suite(certSuite{}) 32 33 func checkNotBefore(c *gc.C, cert *x509.Certificate, now time.Time) { 34 // Check that the certificate is valid from one week before today. 35 c.Check(cert.NotBefore.Before(now), jc.IsTrue) 36 c.Check(cert.NotBefore.Before(now.AddDate(0, 0, -6)), jc.IsTrue) 37 c.Check(cert.NotBefore.After(now.AddDate(0, 0, -8)), jc.IsTrue) 38 } 39 40 func checkNotAfter(c *gc.C, cert *x509.Certificate, expiry time.Time) { 41 // Check the surrounding day. 42 c.Assert(cert.NotAfter.Before(expiry.AddDate(0, 0, 1)), jc.IsTrue) 43 c.Assert(cert.NotAfter.After(expiry.AddDate(0, 0, -1)), jc.IsTrue) 44 } 45 46 func (certSuite) TestParseCertificate(c *gc.C) { 47 xcert, err := cert.ParseCert(caCertPEM) 48 c.Assert(err, jc.ErrorIsNil) 49 c.Assert(xcert.Subject.CommonName, gc.Equals, "juju testing") 50 51 xcert, err = cert.ParseCert(caKeyPEM) 52 c.Check(xcert, gc.IsNil) 53 c.Assert(err, gc.ErrorMatches, "no certificates found") 54 55 xcert, err = cert.ParseCert("hello") 56 c.Check(xcert, gc.IsNil) 57 c.Assert(err, gc.ErrorMatches, "no certificates found") 58 } 59 60 func (certSuite) TestParseCertAndKey(c *gc.C) { 61 xcert, key, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) 62 c.Assert(err, jc.ErrorIsNil) 63 c.Assert(xcert.Subject.CommonName, gc.Equals, "juju testing") 64 c.Assert(key, gc.NotNil) 65 66 c.Assert(xcert.PublicKey.(*rsa.PublicKey), gc.DeepEquals, &key.PublicKey) 67 } 68 69 func (certSuite) TestNewCA(c *gc.C) { 70 now := time.Now() 71 expiry := roundTime(now.AddDate(0, 0, 1)) 72 caCertPEM, caKeyPEM, err := cert.NewCA("foo", "1", expiry) 73 c.Assert(err, jc.ErrorIsNil) 74 75 caCert, caKey, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) 76 c.Assert(err, jc.ErrorIsNil) 77 78 c.Check(caKey, gc.FitsTypeOf, (*rsa.PrivateKey)(nil)) 79 c.Check(caCert.Subject.CommonName, gc.Equals, `juju-generated CA for model "foo"`) 80 checkNotBefore(c, caCert, now) 81 checkNotAfter(c, caCert, expiry) 82 c.Check(caCert.BasicConstraintsValid, jc.IsTrue) 83 c.Check(caCert.IsCA, jc.IsTrue) 84 //c.Assert(caCert.MaxPathLen, Equals, 0) TODO it ends up as -1 - check that this is ok. 85 } 86 87 func (certSuite) TestNewServer(c *gc.C) { 88 now := time.Now() 89 expiry := roundTime(now.AddDate(1, 0, 0)) 90 caCertPEM, caKeyPEM, err := cert.NewCA("foo", "1", expiry) 91 c.Assert(err, jc.ErrorIsNil) 92 93 caCert, _, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) 94 c.Assert(err, jc.ErrorIsNil) 95 96 srvCertPEM, srvKeyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, nil) 97 c.Assert(err, jc.ErrorIsNil) 98 checkCertificate(c, caCert, srvCertPEM, srvKeyPEM, now, expiry) 99 } 100 101 func (certSuite) TestNewDefaultServer(c *gc.C) { 102 now := time.Now() 103 expiry := roundTime(now.AddDate(1, 0, 0)) 104 caCertPEM, caKeyPEM, err := cert.NewCA("foo", "1", expiry) 105 c.Assert(err, jc.ErrorIsNil) 106 107 caCert, _, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) 108 c.Assert(err, jc.ErrorIsNil) 109 110 srvCertPEM, srvKeyPEM, err := cert.NewDefaultServer(caCertPEM, caKeyPEM, nil) 111 c.Assert(err, jc.ErrorIsNil) 112 srvCertExpiry := roundTime(time.Now().AddDate(10, 0, 0)) 113 checkCertificate(c, caCert, srvCertPEM, srvKeyPEM, now, srvCertExpiry) 114 } 115 116 func checkCertificate(c *gc.C, caCert *x509.Certificate, srvCertPEM, srvKeyPEM string, now, expiry time.Time) { 117 srvCert, srvKey, err := cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) 118 c.Assert(err, jc.ErrorIsNil) 119 c.Assert(srvCert.Subject.CommonName, gc.Equals, "*") 120 checkNotBefore(c, srvCert, now) 121 checkNotAfter(c, srvCert, expiry) 122 c.Assert(srvCert.BasicConstraintsValid, jc.IsFalse) 123 c.Assert(srvCert.IsCA, jc.IsFalse) 124 c.Assert(srvCert.ExtKeyUsage, gc.DeepEquals, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}) 125 126 checkTLSConnection(c, caCert, srvCert, srvKey) 127 } 128 129 func (certSuite) TestNewServerHostnames(c *gc.C) { 130 type test struct { 131 hostnames []string 132 expectedDNSNames []string 133 expectedIPAddresses []net.IP 134 } 135 tests := []test{{ 136 []string{}, 137 nil, 138 nil, 139 }, { 140 []string{"example.com"}, 141 []string{"example.com"}, 142 nil, 143 }, { 144 []string{"example.com", "127.0.0.1"}, 145 []string{"example.com"}, 146 []net.IP{net.IPv4(127, 0, 0, 1).To4()}, 147 }, { 148 []string{"::1"}, 149 nil, 150 []net.IP{net.IPv6loopback}, 151 }} 152 for i, t := range tests { 153 c.Logf("test %d: %v", i, t.hostnames) 154 expiry := roundTime(time.Now().AddDate(1, 0, 0)) 155 srvCertPEM, srvKeyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, t.hostnames) 156 c.Assert(err, jc.ErrorIsNil) 157 srvCert, _, err := cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) 158 c.Assert(err, jc.ErrorIsNil) 159 c.Assert(srvCert.DNSNames, gc.DeepEquals, t.expectedDNSNames) 160 c.Assert(srvCert.IPAddresses, gc.DeepEquals, t.expectedIPAddresses) 161 } 162 } 163 164 func (certSuite) TestWithNonUTCExpiry(c *gc.C) { 165 expiry, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", "2012-11-28 15:53:57 +0100 CET") 166 c.Assert(err, jc.ErrorIsNil) 167 certPEM, keyPEM, err := cert.NewCA("foo", "1", expiry) 168 xcert, err := cert.ParseCert(certPEM) 169 c.Assert(err, jc.ErrorIsNil) 170 checkNotAfter(c, xcert, expiry) 171 172 var noHostnames []string 173 certPEM, _, err = cert.NewServer(certPEM, keyPEM, expiry, noHostnames) 174 xcert, err = cert.ParseCert(certPEM) 175 c.Assert(err, jc.ErrorIsNil) 176 checkNotAfter(c, xcert, expiry) 177 } 178 179 func (certSuite) TestNewServerWithInvalidCert(c *gc.C) { 180 var noHostnames []string 181 srvCert, srvKey, err := cert.NewServer(nonCACert, nonCAKey, time.Now(), noHostnames) 182 c.Check(srvCert, gc.Equals, "") 183 c.Check(srvKey, gc.Equals, "") 184 c.Assert(err, gc.ErrorMatches, "CA certificate is not a valid CA") 185 } 186 187 func (certSuite) TestVerify(c *gc.C) { 188 now := time.Now() 189 caCert, caKey, err := cert.NewCA("foo", "1", now.Add(1*time.Minute)) 190 c.Assert(err, jc.ErrorIsNil) 191 192 var noHostnames []string 193 srvCert, _, err := cert.NewServer(caCert, caKey, now.Add(3*time.Minute), noHostnames) 194 c.Assert(err, jc.ErrorIsNil) 195 196 err = cert.Verify(srvCert, caCert, now) 197 c.Assert(err, jc.ErrorIsNil) 198 199 err = cert.Verify(srvCert, caCert, now.Add(55*time.Second)) 200 c.Assert(err, jc.ErrorIsNil) 201 202 err = cert.Verify(srvCert, caCert, now.AddDate(0, 0, -8)) 203 c.Check(err, gc.ErrorMatches, "x509: certificate has expired or is not yet valid") 204 205 err = cert.Verify(srvCert, caCert, now.Add(2*time.Minute)) 206 c.Check(err, gc.ErrorMatches, "x509: certificate has expired or is not yet valid") 207 208 caCert2, caKey2, err := cert.NewCA("bar", "1", now.Add(1*time.Minute)) 209 c.Assert(err, jc.ErrorIsNil) 210 211 // Check original server certificate against wrong CA. 212 err = cert.Verify(srvCert, caCert2, now) 213 c.Check(err, gc.ErrorMatches, "x509: certificate signed by unknown authority") 214 215 srvCert2, _, err := cert.NewServer(caCert2, caKey2, now.Add(1*time.Minute), noHostnames) 216 c.Assert(err, jc.ErrorIsNil) 217 218 // Check new server certificate against original CA. 219 err = cert.Verify(srvCert2, caCert, now) 220 c.Check(err, gc.ErrorMatches, "x509: certificate signed by unknown authority") 221 } 222 223 // checkTLSConnection checks that we can correctly perform a TLS 224 // handshake using the given credentials. 225 func checkTLSConnection(c *gc.C, caCert, srvCert *x509.Certificate, srvKey *rsa.PrivateKey) (caName string) { 226 clientCertPool := x509.NewCertPool() 227 clientCertPool.AddCert(caCert) 228 229 var outBytes bytes.Buffer 230 231 const msg = "hello to the server" 232 p0, p1 := net.Pipe() 233 p0 = &recordingConn{ 234 Conn: p0, 235 Writer: io.MultiWriter(p0, &outBytes), 236 } 237 238 var clientState tls.ConnectionState 239 done := make(chan error) 240 go func() { 241 config := utils.SecureTLSConfig() 242 config.Certificates = []tls.Certificate{{ 243 Certificate: [][]byte{srvCert.Raw}, 244 PrivateKey: srvKey, 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 tlsConfig := utils.SecureTLSConfig() 256 tlsConfig.ServerName = "anyServer" 257 tlsConfig.RootCAs = clientCertPool 258 clientConn := tls.Client(p0, tlsConfig) 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 )