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 )