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 )