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 )