github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 "math/big" 14 "net" 15 "strings" 16 "testing" 17 "time" 18 19 jc "github.com/juju/testing/checkers" 20 "github.com/juju/utils" 21 gc "gopkg.in/check.v1" 22 23 "github.com/juju/juju/cert" 24 ) 25 26 func TestAll(t *testing.T) { 27 gc.TestingT(t) 28 } 29 30 type certSuite struct{} 31 32 var _ = gc.Suite(certSuite{}) 33 34 func checkNotBefore(c *gc.C, cert *x509.Certificate, now time.Time) { 35 // Check that the certificate is valid from one week before today. 36 c.Check(cert.NotBefore.Before(now), jc.IsTrue) 37 c.Check(cert.NotBefore.Before(now.AddDate(0, 0, -6)), jc.IsTrue) 38 c.Check(cert.NotBefore.After(now.AddDate(0, 0, -8)), jc.IsTrue) 39 } 40 41 func checkNotAfter(c *gc.C, cert *x509.Certificate, expiry time.Time) { 42 // Check the surrounding day. 43 c.Assert(cert.NotAfter.Before(expiry.AddDate(0, 0, 1)), jc.IsTrue) 44 c.Assert(cert.NotAfter.After(expiry.AddDate(0, 0, -1)), jc.IsTrue) 45 } 46 47 func (certSuite) TestParseCertificate(c *gc.C) { 48 xcert, err := cert.ParseCert(caCertPEM) 49 c.Assert(err, jc.ErrorIsNil) 50 c.Assert(xcert.Subject.CommonName, gc.Equals, `juju-generated CA for model "juju testing"`) 51 52 xcert, err = cert.ParseCert(caKeyPEM) 53 c.Check(xcert, gc.IsNil) 54 c.Assert(err, gc.ErrorMatches, "no certificates found") 55 56 xcert, err = cert.ParseCert("hello") 57 c.Check(xcert, gc.IsNil) 58 c.Assert(err, gc.ErrorMatches, "no certificates found") 59 } 60 61 func (certSuite) TestParseCertAndKey(c *gc.C) { 62 xcert, key, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) 63 c.Assert(err, jc.ErrorIsNil) 64 c.Assert(xcert.Subject.CommonName, gc.Equals, `juju-generated CA for model "juju testing"`) 65 c.Assert(key, gc.NotNil) 66 67 c.Assert(xcert.PublicKey.(*rsa.PublicKey), gc.DeepEquals, &key.PublicKey) 68 } 69 70 func (certSuite) TestNewCA(c *gc.C) { 71 now := time.Now() 72 expiry := roundTime(now.AddDate(0, 0, 1)) 73 caCertPEM, caKeyPEM, err := cert.NewCA("foo", "1", expiry) 74 c.Assert(err, jc.ErrorIsNil) 75 76 caCert, caKey, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) 77 c.Assert(err, jc.ErrorIsNil) 78 79 c.Check(caKey, gc.FitsTypeOf, (*rsa.PrivateKey)(nil)) 80 c.Check(caCert.Subject.CommonName, gc.Equals, `juju-generated CA for model "foo"`) 81 checkNotBefore(c, caCert, now) 82 checkNotAfter(c, caCert, expiry) 83 c.Check(caCert.BasicConstraintsValid, jc.IsTrue) 84 c.Check(caCert.IsCA, jc.IsTrue) 85 //c.Assert(caCert.MaxPathLen, Equals, 0) TODO it ends up as -1 - check that this is ok. 86 } 87 88 func (certSuite) TestNewServer(c *gc.C) { 89 now := time.Now() 90 expiry := roundTime(now.AddDate(1, 0, 0)) 91 caCertPEM, caKeyPEM, err := cert.NewCA("foo", "1", expiry) 92 c.Assert(err, jc.ErrorIsNil) 93 94 caCert, _, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) 95 c.Assert(err, jc.ErrorIsNil) 96 97 srvCertPEM, srvKeyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, nil) 98 c.Assert(err, jc.ErrorIsNil) 99 checkCertificate(c, caCert, srvCertPEM, srvKeyPEM, now, expiry) 100 } 101 102 func (certSuite) TestNewDefaultServer(c *gc.C) { 103 now := time.Now() 104 expiry := roundTime(now.AddDate(1, 0, 0)) 105 caCertPEM, caKeyPEM, err := cert.NewCA("foo", "1", expiry) 106 c.Assert(err, jc.ErrorIsNil) 107 108 caCert, _, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) 109 c.Assert(err, jc.ErrorIsNil) 110 111 srvCertPEM, srvKeyPEM, err := cert.NewDefaultServer(caCertPEM, caKeyPEM, nil) 112 c.Assert(err, jc.ErrorIsNil) 113 srvCertExpiry := roundTime(time.Now().AddDate(10, 0, 0)) 114 checkCertificate(c, caCert, srvCertPEM, srvKeyPEM, now, srvCertExpiry) 115 } 116 117 func checkCertificate(c *gc.C, caCert *x509.Certificate, srvCertPEM, srvKeyPEM string, now, expiry time.Time) { 118 srvCert, srvKey, err := cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) 119 c.Assert(err, jc.ErrorIsNil) 120 c.Assert(srvCert.Subject.CommonName, gc.Equals, "*") 121 checkNotBefore(c, srvCert, now) 122 checkNotAfter(c, srvCert, expiry) 123 c.Assert(srvCert.BasicConstraintsValid, jc.IsFalse) 124 c.Assert(srvCert.IsCA, jc.IsFalse) 125 c.Assert(srvCert.ExtKeyUsage, gc.DeepEquals, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}) 126 c.Assert(srvCert.SerialNumber, gc.NotNil) 127 if srvCert.SerialNumber.Cmp(big.NewInt(0)) == 0 { 128 c.Fatalf("zero serial number") 129 } 130 131 checkTLSConnection(c, caCert, srvCert, srvKey) 132 } 133 134 func (certSuite) TestNewServerHostnames(c *gc.C) { 135 type test struct { 136 hostnames []string 137 expectedDNSNames []string 138 expectedIPAddresses []net.IP 139 } 140 tests := []test{{ 141 []string{}, 142 nil, 143 nil, 144 }, { 145 []string{"example.com"}, 146 []string{"example.com"}, 147 nil, 148 }, { 149 []string{"example.com", "127.0.0.1"}, 150 []string{"example.com"}, 151 []net.IP{net.IPv4(127, 0, 0, 1).To4()}, 152 }, { 153 []string{"::1"}, 154 nil, 155 []net.IP{net.IPv6loopback}, 156 }} 157 for i, t := range tests { 158 c.Logf("test %d: %v", i, t.hostnames) 159 expiry := roundTime(time.Now().AddDate(1, 0, 0)) 160 srvCertPEM, srvKeyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, t.hostnames) 161 c.Assert(err, jc.ErrorIsNil) 162 srvCert, _, err := cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) 163 c.Assert(err, jc.ErrorIsNil) 164 c.Assert(srvCert.DNSNames, gc.DeepEquals, t.expectedDNSNames) 165 c.Assert(srvCert.IPAddresses, gc.DeepEquals, t.expectedIPAddresses) 166 } 167 } 168 169 func (certSuite) TestWithNonUTCExpiry(c *gc.C) { 170 expiry, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", "2012-11-28 15:53:57 +0100 CET") 171 c.Assert(err, jc.ErrorIsNil) 172 certPEM, keyPEM, err := cert.NewCA("foo", "1", expiry) 173 xcert, err := cert.ParseCert(certPEM) 174 c.Assert(err, jc.ErrorIsNil) 175 checkNotAfter(c, xcert, expiry) 176 177 var noHostnames []string 178 certPEM, _, err = cert.NewServer(certPEM, keyPEM, expiry, noHostnames) 179 xcert, err = cert.ParseCert(certPEM) 180 c.Assert(err, jc.ErrorIsNil) 181 checkNotAfter(c, xcert, expiry) 182 } 183 184 func (certSuite) TestNewServerWithInvalidCert(c *gc.C) { 185 var noHostnames []string 186 srvCert, srvKey, err := cert.NewServer(nonCACert, nonCAKey, time.Now(), noHostnames) 187 c.Check(srvCert, gc.Equals, "") 188 c.Check(srvKey, gc.Equals, "") 189 c.Assert(err, gc.ErrorMatches, "CA certificate is not a valid CA") 190 } 191 192 func (certSuite) TestVerify(c *gc.C) { 193 now := time.Now() 194 caCert, caKey, err := cert.NewCA("foo", "1", now.Add(1*time.Minute)) 195 c.Assert(err, jc.ErrorIsNil) 196 197 var noHostnames []string 198 srvCert, _, err := cert.NewServer(caCert, caKey, now.Add(3*time.Minute), noHostnames) 199 c.Assert(err, jc.ErrorIsNil) 200 201 err = cert.Verify(srvCert, caCert, now) 202 c.Assert(err, jc.ErrorIsNil) 203 204 err = cert.Verify(srvCert, caCert, now.Add(55*time.Second)) 205 c.Assert(err, jc.ErrorIsNil) 206 207 err = cert.Verify(srvCert, caCert, now.AddDate(0, 0, -8)) 208 c.Check(err, gc.ErrorMatches, "x509: certificate has expired or is not yet valid") 209 210 err = cert.Verify(srvCert, caCert, now.Add(2*time.Minute)) 211 c.Check(err, gc.ErrorMatches, "x509: certificate has expired or is not yet valid") 212 213 caCert2, caKey2, err := cert.NewCA("bar", "1", now.Add(1*time.Minute)) 214 c.Assert(err, jc.ErrorIsNil) 215 216 // Check original server certificate against wrong CA. 217 err = cert.Verify(srvCert, caCert2, now) 218 c.Check(err, gc.ErrorMatches, "x509: certificate signed by unknown authority") 219 220 srvCert2, _, err := cert.NewServer(caCert2, caKey2, now.Add(1*time.Minute), noHostnames) 221 c.Assert(err, jc.ErrorIsNil) 222 223 // Check new server certificate against original CA. 224 err = cert.Verify(srvCert2, caCert, now) 225 c.Check(err, gc.ErrorMatches, "x509: certificate signed by unknown authority") 226 } 227 228 // checkTLSConnection checks that we can correctly perform a TLS 229 // handshake using the given credentials. 230 func checkTLSConnection(c *gc.C, caCert, srvCert *x509.Certificate, srvKey *rsa.PrivateKey) (caName string) { 231 clientCertPool := x509.NewCertPool() 232 clientCertPool.AddCert(caCert) 233 234 var outBytes bytes.Buffer 235 236 const msg = "hello to the server" 237 p0, p1 := net.Pipe() 238 p0 = &recordingConn{ 239 Conn: p0, 240 Writer: io.MultiWriter(p0, &outBytes), 241 } 242 243 var clientState tls.ConnectionState 244 done := make(chan error) 245 go func() { 246 config := utils.SecureTLSConfig() 247 config.Certificates = []tls.Certificate{{ 248 Certificate: [][]byte{srvCert.Raw}, 249 PrivateKey: srvKey, 250 }} 251 252 conn := tls.Server(p1, config) 253 defer conn.Close() 254 data, err := ioutil.ReadAll(conn) 255 c.Assert(err, jc.ErrorIsNil) 256 c.Assert(string(data), gc.Equals, msg) 257 close(done) 258 }() 259 260 tlsConfig := utils.SecureTLSConfig() 261 tlsConfig.ServerName = "anyServer" 262 tlsConfig.RootCAs = clientCertPool 263 clientConn := tls.Client(p0, tlsConfig) 264 defer clientConn.Close() 265 266 _, err := clientConn.Write([]byte(msg)) 267 c.Assert(err, jc.ErrorIsNil) 268 clientState = clientConn.ConnectionState() 269 clientConn.Close() 270 271 // wait for server to exit 272 <-done 273 274 outData := outBytes.String() 275 c.Assert(outData, gc.Not(gc.HasLen), 0) 276 if strings.Index(outData, msg) != -1 { 277 c.Fatalf("TLS connection not encrypted") 278 } 279 c.Assert(clientState.VerifiedChains, gc.HasLen, 1) 280 c.Assert(clientState.VerifiedChains[0], gc.HasLen, 2) 281 return clientState.VerifiedChains[0][1].Subject.CommonName 282 } 283 284 type recordingConn struct { 285 net.Conn 286 io.Writer 287 } 288 289 func (c recordingConn) Write(buf []byte) (int, error) { 290 return c.Writer.Write(buf) 291 } 292 293 // roundTime returns t rounded to the previous whole second. 294 func roundTime(t time.Time) time.Time { 295 return t.Add(time.Duration(-t.Nanosecond())) 296 } 297 298 var ( 299 caCertPEM = ` 300 -----BEGIN CERTIFICATE----- 301 MIICHDCCAcagAwIBAgIUfzWn5ktGMxD6OiTgfiZyvKdM+ZYwDQYJKoZIhvcNAQEL 302 BQAwazENMAsGA1UEChMEanVqdTEzMDEGA1UEAwwqanVqdS1nZW5lcmF0ZWQgQ0Eg 303 Zm9yIG1vZGVsICJqdWp1IHRlc3RpbmciMSUwIwYDVQQFExwxMjM0LUFCQ0QtSVMt 304 Tk9ULUEtUkVBTC1VVUlEMB4XDTE2MDkyMTEwNDgyN1oXDTI2MDkyODEwNDgyN1ow 305 azENMAsGA1UEChMEanVqdTEzMDEGA1UEAwwqanVqdS1nZW5lcmF0ZWQgQ0EgZm9y 306 IG1vZGVsICJqdWp1IHRlc3RpbmciMSUwIwYDVQQFExwxMjM0LUFCQ0QtSVMtTk9U 307 LUEtUkVBTC1VVUlEMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+0X+1zl2vt1wI4 308 1Q+RnlltJyaJmtwCbHRhREXVGU7t0kTMMNERxqLnuNUyWRz90Rg8s9XvOtCqNYW7 309 mypGrFECAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMBAf8w 310 HQYDVR0OBBYEFHueMLZ1QJ/2sKiPIJ28TzjIMRENMA0GCSqGSIb3DQEBCwUAA0EA 311 ovZN0RbUHrO8q9Eazh0qPO4mwW9jbGTDz126uNrLoz1g3TyWxIas1wRJ8IbCgxLy 312 XUrBZO5UPZab66lJWXyseA== 313 -----END CERTIFICATE----- 314 ` 315 316 caKeyPEM = ` 317 -----BEGIN RSA PRIVATE KEY----- 318 MIIBOgIBAAJBAL+0X+1zl2vt1wI41Q+RnlltJyaJmtwCbHRhREXVGU7t0kTMMNER 319 xqLnuNUyWRz90Rg8s9XvOtCqNYW7mypGrFECAwEAAQJAMPa+JaUHgO6foxam/LIB 320 0u95N3OgFR+dWeBaEsgKDclpREdJ0rXNI+3C3kwqeEZR4omoPlBeSEewSkwHxpmI 321 0QIhAOjKiHZ5v6R8haleipbDzkGUnZW07hEwL5Ld4MNx/QQ1AiEA0tEzSSNAdM0C 322 M/vY0x5mekIYai8/tFSEG9PJ3ZkpEy0CIQCo9B3YxwI1Un777vbs903iQQeiWP+U 323 EAHnOQvhLgDxpQIgGkpml+9igW5zoOH+h02aQBLwEoXz7tw/YW0HFrCcE70CIGkS 324 ve4WjiEqnQaHNAPy0hY/1DfIgBOSpOfnkFHOk9vX 325 -----END RSA PRIVATE KEY----- 326 ` 327 328 nonCACert = ` 329 -----BEGIN CERTIFICATE----- 330 MIIB8jCCAZygAwIBAgIVANueMZWTFEIx6AcNAWsG4VL4sUn5MA0GCSqGSIb3DQEB 331 CwUAMGsxDTALBgNVBAoTBGp1anUxMzAxBgNVBAMMKmp1anUtZ2VuZXJhdGVkIENB 332 IGZvciBtb2RlbCAianVqdSB0ZXN0aW5nIjElMCMGA1UEBRMcMTIzNC1BQkNELUlT 333 LU5PVC1BLVJFQUwtVVVJRDAeFw0xNjA5MjExMDQ4MjdaFw0yNjA5MjgxMDQ4Mjda 334 MBsxDTALBgNVBAoTBGp1anUxCjAIBgNVBAMTASowXDANBgkqhkiG9w0BAQEFAANL 335 ADBIAkEAwZps3qpPu2FCAhbxolf/BvSa+dMal3AhPMe+lwTuSbtS81W+WSrbwUSI 336 ZKSGHYDpFRN6ytNjt1oPbDNKDIR30wIDAQABo2cwZTAOBgNVHQ8BAf8EBAMCA6gw 337 EwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYEFNNUDrcyP/4RbGBpKeC3gmfL 338 kjlwMB8GA1UdIwQYMBaAFHueMLZ1QJ/2sKiPIJ28TzjIMRENMA0GCSqGSIb3DQEB 339 CwUAA0EALiurKx//Qh5TQQ0TmT0P5f7OFLIs5XPSS98Lseb92h12CPNO4kB000Yh 340 Xa7kZRGngwFbvjzqZ0eOfmo0l8M23A== 341 -----END CERTIFICATE----- 342 ` 343 344 nonCAKey = ` 345 -----BEGIN RSA PRIVATE KEY----- 346 MIIBOwIBAAJBAMGabN6qT7thQgIW8aJX/wb0mvnTGpdwITzHvpcE7km7UvNVvlkq 347 28FEiGSkhh2A6RUTesrTY7daD2wzSgyEd9MCAwEAAQJBAKfeuOvRjVUSneOl9Vsp 348 Je7oBcD9dR8+kPNc1zungN7YVhIuxqvzXJSPeMGsHloPI+BcFFXv3t+eVCDT9sPL 349 L+ECIQDq1nqVIEX3k5nn6eI0L5CQbIfEyvWGJ/mOGSo9TWdN+QIhANMMsopPb9ct 350 Z61LqPmTtNX4nhHyMEjxbUzqzsZzsRcrAiBeYyhP6fHVSXERopK1kOyU79o+Aalf 351 a4/FSl4M16CO2QIgOBQZpNKyvxRbhhqijZ6H4IstRUt7NQahqlyCEQ1Qsv0CIQDQ 352 tUzgFwUpd6NVButkqWGqnmBeKUOs97dqSyOzN9Nk8w== 353 -----END RSA PRIVATE KEY----- 354 ` 355 )