Beispiel #1
0
func TestCheckCert(t *testing.T) {
	testKey, _ := rsa.GenerateKey(rand.Reader, 1024)
	checker := newChecker(nil)
	fc := clock.NewFake()
	fc.Add(time.Hour * 24 * 90)
	checker.clock = fc

	issued := checker.clock.Now().Add(-time.Hour * 24 * 45)
	goodExpiry := issued.Add(checkPeriod)
	serial := big.NewInt(1337)
	// Problems
	//   Blacklsited common name
	//   Expiry period is too long
	//   Basic Constraints aren't set
	//   Wrong key usage (none)
	rawCert := x509.Certificate{
		Subject: pkix.Name{
			CommonName: "example.com",
		},
		NotAfter:              goodExpiry.AddDate(0, 0, 1), // Period too long
		DNSNames:              []string{"example-a.com"},
		SerialNumber:          serial,
		BasicConstraintsValid: false,
	}
	brokenCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
	test.AssertNotError(t, err, "Couldn't create certificate")
	// Problems
	//   Digest doesn't match
	//   Serial doesn't match
	//   Expiry doesn't match
	cert := core.Certificate{
		Status:  core.StatusValid,
		DER:     brokenCertDer,
		Issued:  issued,
		Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match
	}

	problems := checker.checkCert(cert)
	test.AssertEquals(t, len(problems), 7)

	// Fix the problems
	rawCert.Subject.CommonName = "example-a.com"
	rawCert.NotAfter = goodExpiry
	rawCert.BasicConstraintsValid = true
	rawCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	goodCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
	test.AssertNotError(t, err, "Couldn't create certificate")
	parsed, err := x509.ParseCertificate(goodCertDer)
	test.AssertNotError(t, err, "Couldn't parse created certificate")
	cert.Serial = core.SerialToString(serial)
	cert.Digest = core.Fingerprint256(goodCertDer)
	cert.DER = goodCertDer
	cert.Expires = parsed.NotAfter
	problems = checker.checkCert(cert)
	test.AssertEquals(t, len(problems), 0)
}
func createCertificate(d *schema.ResourceData, template, parent *x509.Certificate, pub crypto.PublicKey, priv interface{}) error {
	var err error

	template.NotBefore = time.Now()
	template.NotAfter = template.NotBefore.Add(time.Duration(d.Get("validity_period_hours").(int)) * time.Hour)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	template.SerialNumber, err = rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return fmt.Errorf("failed to generate serial number: %s", err)
	}

	keyUsesI := d.Get("allowed_uses").([]interface{})
	for _, keyUseI := range keyUsesI {
		keyUse := keyUseI.(string)
		if usage, ok := keyUsages[keyUse]; ok {
			template.KeyUsage |= usage
		}
		if usage, ok := extKeyUsages[keyUse]; ok {
			template.ExtKeyUsage = append(template.ExtKeyUsage, usage)
		}
	}

	if d.Get("is_ca_certificate").(bool) {
		template.IsCA = true

		template.SubjectKeyId, err = generateSubjectKeyID(pub)
		if err != nil {
			return fmt.Errorf("failed to set subject key identifier: %s", err)
		}
	}

	certBytes, err := x509.CreateCertificate(rand.Reader, template, parent, pub, priv)
	if err != nil {
		return fmt.Errorf("error creating certificate: %s", err)
	}
	certPem := string(pem.EncodeToMemory(&pem.Block{Type: pemCertType, Bytes: certBytes}))

	validFromBytes, err := template.NotBefore.MarshalText()
	if err != nil {
		return fmt.Errorf("error serializing validity_start_time: %s", err)
	}
	validToBytes, err := template.NotAfter.MarshalText()
	if err != nil {
		return fmt.Errorf("error serializing validity_end_time: %s", err)
	}

	d.SetId(template.SerialNumber.String())
	d.Set("cert_pem", certPem)
	d.Set("validity_start_time", string(validFromBytes))
	d.Set("validity_end_time", string(validToBytes))

	return nil
}
// When comparing certificates created at different times for equality, we do
// not want to worry about fields which are dependent on the time of creation.
// Thus we nullify these fields before comparing the certificates.
func nullifyTimeDependency(cert *x509.Certificate) *x509.Certificate {
	cert.Raw = nil
	cert.RawTBSCertificate = nil
	cert.RawSubjectPublicKeyInfo = nil
	cert.Signature = nil
	cert.PublicKey = nil
	cert.SerialNumber = nil
	cert.NotBefore = time.Time{}
	cert.NotAfter = time.Time{}
	cert.Extensions = nil
	cert.SubjectKeyId = nil
	cert.AuthorityKeyId = nil

	return cert
}
Beispiel #4
0
// FillTemplate is a utility function that tries to load as much of
// the certificate template as possible from the profiles and current
// template. It fills in the key uses, expiration, revocation URLs
// and SKI.
func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile) error {
	ski, err := ComputeSKI(template)

	var (
		eku             []x509.ExtKeyUsage
		ku              x509.KeyUsage
		backdate        time.Duration
		expiry          time.Duration
		notBefore       time.Time
		notAfter        time.Time
		crlURL, ocspURL string
	)

	// The third value returned from Usages is a list of unknown key usages.
	// This should be used when validating the profile at load, and isn't used
	// here.
	ku, eku, _ = profile.Usages()
	if profile.IssuerURL == nil {
		profile.IssuerURL = defaultProfile.IssuerURL
	}

	if ku == 0 && len(eku) == 0 {
		return cferr.New(cferr.PolicyError, cferr.NoKeyUsages)
	}

	if expiry = profile.Expiry; expiry == 0 {
		expiry = defaultProfile.Expiry
	}

	if crlURL = profile.CRL; crlURL == "" {
		crlURL = defaultProfile.CRL
	}
	if ocspURL = profile.OCSP; ocspURL == "" {
		ocspURL = defaultProfile.OCSP
	}
	if backdate = profile.Backdate; backdate == 0 {
		backdate = -5 * time.Minute
	} else {
		backdate = -1 * profile.Backdate
	}

	if !profile.NotBefore.IsZero() {
		notBefore = profile.NotBefore.UTC()
	} else {
		notBefore = time.Now().Round(time.Minute).Add(backdate).UTC()
	}

	if !profile.NotAfter.IsZero() {
		notAfter = profile.NotAfter.UTC()
	} else {
		notAfter = notBefore.Add(expiry).UTC()
	}

	template.NotBefore = notBefore
	template.NotAfter = notAfter
	template.KeyUsage = ku
	template.ExtKeyUsage = eku
	template.BasicConstraintsValid = true
	template.IsCA = profile.CA
	template.SubjectKeyId = ski

	if ocspURL != "" {
		template.OCSPServer = []string{ocspURL}
	}
	if crlURL != "" {
		template.CRLDistributionPoints = []string{crlURL}
	}

	if len(profile.IssuerURL) != 0 {
		template.IssuingCertificateURL = profile.IssuerURL
	}
	if len(profile.Policies) != 0 {
		err = addPolicies(template, profile.Policies)
		if err != nil {
			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
		}
	}
	if profile.OCSPNoCheck {
		ocspNoCheckExtension := pkix.Extension{
			Id:       asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5},
			Critical: false,
			Value:    []byte{0x05, 0x00},
		}
		template.ExtraExtensions = append(template.ExtraExtensions, ocspNoCheckExtension)
	}

	return nil
}
Beispiel #5
0
func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile) (cert []byte, err error) {
	pub := template.PublicKey
	encodedpub, err := x509.MarshalPKIXPublicKey(pub)
	if err != nil {
		return
	}
	pubhash := sha1.New()
	pubhash.Write(encodedpub)

	if profile == nil {
		profile = s.Policy.Default
	}

	var (
		eku             []x509.ExtKeyUsage
		ku              x509.KeyUsage
		expiry          time.Duration
		crlURL, ocspURL string
	)

	// The third value returned from Usages is a list of unknown key usages.
	// This should be used when validating the profile at load, and isn't used
	// here.
	ku, eku, _ = profile.Usages()
	expiry = profile.Expiry
	if profile.IssuerURL == nil {
		profile.IssuerURL = s.Policy.Default.IssuerURL
	}

	if ku == 0 && len(eku) == 0 {
		err = cferr.New(cferr.PolicyError, cferr.NoKeyUsages, errors.New("no key usage available"))
		return
	}

	if expiry == 0 {
		expiry = s.Policy.Default.Expiry
	}

	if crlURL = profile.CRL; crlURL == "" {
		crlURL = s.Policy.Default.CRL
	}
	if ocspURL = profile.OCSP; ocspURL == "" {
		ocspURL = s.Policy.Default.OCSP
	}

	now := time.Now()
	serialNumber, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
	if err != nil {
		err = cferr.New(cferr.CertificateError, cferr.Unknown, err)
	}

	template.SerialNumber = serialNumber
	template.NotBefore = now.Add(-5 * time.Minute).UTC()
	template.NotAfter = now.Add(expiry).UTC()
	template.KeyUsage = ku
	template.ExtKeyUsage = eku
	template.BasicConstraintsValid = true
	template.IsCA = profile.CA
	template.SubjectKeyId = pubhash.Sum(nil)

	if ocspURL != "" {
		template.OCSPServer = []string{ocspURL}
	}
	if crlURL != "" {
		template.CRLDistributionPoints = []string{crlURL}
	}

	if len(profile.IssuerURL) != 0 {
		template.IssuingCertificateURL = profile.IssuerURL
	}

	var initRoot bool
	if s.CA == nil {
		if !template.IsCA {
			err = cferr.New(cferr.PolicyError, cferr.InvalidRequest, nil)
			return
		}
		template.DNSNames = nil
		s.CA = template
		initRoot = true
		template.MaxPathLen = 2
	} else if template.IsCA {
		template.MaxPathLen = 1
		template.DNSNames = nil
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, template, s.CA, pub, s.Priv)
	if err != nil {
		return
	}
	if initRoot {
		s.CA, err = x509.ParseCertificate(derBytes)
		if err != nil {
			err = cferr.New(cferr.CertificateError, cferr.ParseFailed, err)
			return
		}
	}
	cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	return
}
func TestCheckCert(t *testing.T) {
	saDbMap, err := sa.NewDbMap(vars.DBConnSA, 0)
	test.AssertNotError(t, err, "Couldn't connect to database")
	saCleanup := test.ResetSATestDatabase(t)
	defer func() {
		saCleanup()
	}()

	testKey, _ := rsa.GenerateKey(rand.Reader, 1024)
	fc := clock.NewFake()
	fc.Add(time.Hour * 24 * 90)

	checker := newChecker(saDbMap, fc, pa, expectedValidityPeriod)

	issued := checker.clock.Now().Add(-time.Hour * 24 * 45)
	goodExpiry := issued.Add(expectedValidityPeriod)
	serial := big.NewInt(1337)
	// Problems
	//   Expiry period is too long
	//   Basic Constraints aren't set
	//   Wrong key usage (none)
	rawCert := x509.Certificate{
		Subject: pkix.Name{
			CommonName: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeexample.com",
		},
		NotBefore:             issued,
		NotAfter:              goodExpiry.AddDate(0, 0, 1), // Period too long
		DNSNames:              []string{"example-a.com"},
		SerialNumber:          serial,
		BasicConstraintsValid: false,
	}
	brokenCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
	test.AssertNotError(t, err, "Couldn't create certificate")
	// Problems
	//   Digest doesn't match
	//   Serial doesn't match
	//   Expiry doesn't match
	//   Issued doesn't match
	cert := core.Certificate{
		Serial:  "8485f2687eba29ad455ae4e31c8679206fec",
		DER:     brokenCertDer,
		Issued:  issued.Add(12 * time.Hour),
		Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match
	}

	problems := checker.checkCert(cert)

	problemsMap := map[string]int{
		"Stored digest doesn't match certificate digest":                            1,
		"Stored serial doesn't match certificate serial":                            1,
		"Stored expiration doesn't match certificate NotAfter":                      1,
		"Certificate doesn't have basic constraints set":                            1,
		"Certificate has a validity period longer than 2160h0m0s":                   1,
		"Stored issuance date is outside of 6 hour window of certificate NotBefore": 1,
		"Certificate has incorrect key usage extensions":                            1,
		"Certificate has common name >64 characters long (65)":                      1,
	}
	for _, p := range problems {
		_, ok := problemsMap[p]
		if !ok {
			t.Errorf("Found unexpected problem '%s'.", p)
		}
		delete(problemsMap, p)
	}
	for k := range problemsMap {
		t.Errorf("Expected problem but didn't find it: '%s'.", k)
	}
	test.AssertEquals(t, len(problems), 8)

	// Same settings as above, but the stored serial number in the DB is invalid.
	cert.Serial = "not valid"
	problems = checker.checkCert(cert)
	foundInvalidSerialProblem := false
	for _, p := range problems {
		if p == "Stored serial is invalid" {
			foundInvalidSerialProblem = true
		}
	}
	test.Assert(t, foundInvalidSerialProblem, "Invalid certificate serial number in DB did not trigger problem.")

	// Fix the problems
	rawCert.Subject.CommonName = "example-a.com"
	rawCert.NotAfter = goodExpiry
	rawCert.BasicConstraintsValid = true
	rawCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	goodCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
	test.AssertNotError(t, err, "Couldn't create certificate")
	parsed, err := x509.ParseCertificate(goodCertDer)
	test.AssertNotError(t, err, "Couldn't parse created certificate")
	cert.Serial = core.SerialToString(serial)
	cert.Digest = core.Fingerprint256(goodCertDer)
	cert.DER = goodCertDer
	cert.Expires = parsed.NotAfter
	cert.Issued = parsed.NotBefore
	problems = checker.checkCert(cert)
	test.AssertEquals(t, len(problems), 0)
}
Beispiel #7
0
func SignNewCertificate(privateKey *PrivateKey, template *x509.Certificate, signer *x509.Certificate, signerPrivateKey *PrivateKey) (*Certificate, error) {
	if template.PublicKey == nil {
		rsaPrivateKey, ok := privateKey.Key.(*rsa.PrivateKey)
		if ok {
			template.PublicKey = rsaPrivateKey.Public()
		}
	}

	if template.PublicKey == nil {
		return nil, fmt.Errorf("PublicKey not set, and cannot be determined from %T", privateKey)
	}

	now := time.Now()
	if template.NotBefore.IsZero() {
		template.NotBefore = now.Add(time.Hour * -48)
	}

	if template.NotAfter.IsZero() {
		template.NotAfter = now.Add(time.Hour * 10 * 365 * 24)
	}

	if template.SerialNumber == nil {
		serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
		serialNumber, err := crypto_rand.Int(crypto_rand.Reader, serialNumberLimit)
		if err != nil {
			return nil, fmt.Errorf("error generating certificate serial number: %s", err)
		}
		template.SerialNumber = serialNumber
	}
	var parent *x509.Certificate
	if signer != nil {
		parent = signer
	} else {
		parent = template
		signerPrivateKey = privateKey
	}

	if template.KeyUsage == 0 {
		template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
	}

	if template.ExtKeyUsage == nil {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
	}
	//c.SignatureAlgorithm  = do we want to overrride?

	certificateData, err := x509.CreateCertificate(crypto_rand.Reader, template, parent, template.PublicKey, signerPrivateKey.Key)
	if err != nil {
		return nil, fmt.Errorf("error creating certificate: %v", err)
	}

	c := &Certificate{}
	c.PublicKey = template.PublicKey

	cert, err := x509.ParseCertificate(certificateData)
	if err != nil {
		return nil, fmt.Errorf("error parsing certificate: %v", err)
	}
	c.Certificate = cert

	return c, nil
}
Beispiel #8
0
func TestCheckCert(t *testing.T) {
	saDbMap, err := sa.NewDbMap(saDbConnStr)
	test.AssertNotError(t, err, "Couldn't connect to database")
	saCleanup := test.ResetTestDatabase(t, saDbMap.Db)
	paDbMap, err := sa.NewDbMap(paDbConnStr)
	test.AssertNotError(t, err, "Couldn't connect to policy database")
	paCleanup := test.ResetTestDatabase(t, paDbMap.Db)
	defer func() {
		saCleanup()
		paCleanup()
	}()

	testKey, _ := rsa.GenerateKey(rand.Reader, 1024)
	fc := clock.NewFake()
	fc.Add(time.Hour * 24 * 90)

	checker := newChecker(saDbMap, paDbMap, fc, false)

	issued := checker.clock.Now().Add(-time.Hour * 24 * 45)
	goodExpiry := issued.Add(checkPeriod)
	serial := big.NewInt(1337)
	// Problems
	//   Expiry period is too long
	//   Basic Constraints aren't set
	//   Wrong key usage (none)
	rawCert := x509.Certificate{
		Subject: pkix.Name{
			CommonName: "example.com",
		},
		NotBefore:             issued,
		NotAfter:              goodExpiry.AddDate(0, 0, 1), // Period too long
		DNSNames:              []string{"example-a.com"},
		SerialNumber:          serial,
		BasicConstraintsValid: false,
	}
	brokenCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
	test.AssertNotError(t, err, "Couldn't create certificate")
	// Problems
	//   Digest doesn't match
	//   Serial doesn't match
	//   Expiry doesn't match
	//   Issued doesn't match
	cert := core.Certificate{
		DER:     brokenCertDer,
		Issued:  issued.Add(12 * time.Hour),
		Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match
	}

	problems := checker.checkCert(cert)
	fmt.Println(strings.Join(problems, "\n"))
	test.AssertEquals(t, len(problems), 7)

	// Fix the problems
	rawCert.Subject.CommonName = "example-a.com"
	rawCert.NotAfter = goodExpiry
	rawCert.BasicConstraintsValid = true
	rawCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	goodCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
	test.AssertNotError(t, err, "Couldn't create certificate")
	parsed, err := x509.ParseCertificate(goodCertDer)
	test.AssertNotError(t, err, "Couldn't parse created certificate")
	cert.Serial = core.SerialToString(serial)
	cert.Digest = core.Fingerprint256(goodCertDer)
	cert.DER = goodCertDer
	cert.Expires = parsed.NotAfter
	cert.Issued = parsed.NotBefore
	problems = checker.checkCert(cert)
	test.AssertEquals(t, len(problems), 0)
}
Beispiel #9
0
// Generates a new self-signed private/public key and returns (cert, key, error).
func generateCertificate() ([]byte, []byte, error) {
	template := x509.Certificate{
		Subject: pkix.Name{
			Organization: []string{"ffsend"},
		},
		NotBefore: time.Now(),

		KeyUsage: x509.KeyUsageCertSign |
			x509.KeyUsageKeyEncipherment |
			x509.KeyUsageDigitalSignature,
		ExtKeyUsage: []x509.ExtKeyUsage{
			x509.ExtKeyUsageServerAuth,
			x509.ExtKeyUsageClientAuth,
		},
		BasicConstraintsValid: true,

		IsCA: true,
	}

	// Set the common name to our hostname.
	host, err := os.Hostname()
	if err != nil {
		return nil, nil, err
	}
	template.Subject.CommonName = host
	// TODO: maybe append to IPAddresses / DNSNames with interfaces?

	// We expire after a year.
	template.NotAfter = template.NotBefore.Add(time.Hour * 24 * 365)

	priv, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	template.SerialNumber, err = rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, nil, err
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		return nil, nil, err
	}

	// Great, all success!
	var certBuf, keyBuf bytes.Buffer
	if err = pem.Encode(
		&certBuf,
		&pem.Block{
			Type:  "CERTIFICATE",
			Bytes: derBytes,
		},
	); err != nil {
		return nil, nil, err
	}

	if err = pem.Encode(
		&keyBuf,
		&pem.Block{
			Type:  "RSA PRIVATE KEY",
			Bytes: x509.MarshalPKCS1PrivateKey(priv),
		},
	); err != nil {
		return nil, nil, err
	}

	return certBuf.Bytes(), keyBuf.Bytes(), nil
}
Beispiel #10
0
func main() {

	organisation := "acmecorp"

	// Common Name for the certificate. The common name
	// can be anything, but is usually set to the server's primary
	// DNS name. Even if you plan to connect via IP address you
	// should specify the DNS name here.
	commonName := "localhost"

	// additional DNS names and IP
	// addresses that clients may use to connect to the server. If
	// you plan to connect to the server via IP address and not DNS
	// then you must specify those IP addresses here.
	ipaddressOrDNSNames := []string{"127.0.0.1", "localhost"}

	// How long should the certificate be valid for? A year (365
	// days) is usual but requires the certificate to be regenerated
	// within a year or the certificate will cease working.
	validInDays := 365

	template := x509.Certificate{
		Subject: pkix.Name{
			Organization: []string{organisation},
			CommonName:   commonName,
		},
		NotBefore: time.Now(),

		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,

		IsCA: true,
	}

	for _, val := range ipaddressOrDNSNames {
		if ip := net.ParseIP(val); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, val)
		}
	}

	template.NotAfter = template.NotBefore.Add(time.Duration(validInDays) * time.Hour * 24)

	spew.Dump(template)

	server_path := "../server"
	client_path := "../client"

	if _, err := os.Stat(server_path); os.IsNotExist(err) {
		os.Mkdir(server_path, 0700)
	}
	if _, err := os.Stat(client_path); os.IsNotExist(err) {
		os.Mkdir(client_path, 0700)
	}

	generateCerts(template, server_path)

	template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}

	generateCerts(template, client_path)
}
Beispiel #11
0
func main() {
	var err error

	template := x509.Certificate{
		Subject: pkix.Name{
			Organization: []string{"Log Courier"},
		},
		NotBefore: time.Now(),

		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,

		IsCA: true,
	}

	fmt.Println("Specify the Common Name for the certificate. The common name")
	fmt.Println("can be anything, but is usually set to the server's primary")
	fmt.Println("DNS name. Even if you plan to connect via IP address you")
	fmt.Println("should specify the DNS name here.")
	fmt.Println()

	template.Subject.CommonName = readString("Common name")
	fmt.Println()

	fmt.Println("The next step is to add any additional DNS names and IP")
	fmt.Println("addresses that clients may use to connect to the server. If")
	fmt.Println("you plan to connect to the server via IP address and not DNS")
	fmt.Println("then you must specify those IP addresses here.")
	fmt.Println("When you are finished, just press enter.")
	fmt.Println()

	var cnt = 0
	var val string
	for {
		cnt++

		if val = readString(fmt.Sprintf("DNS or IP address %d", cnt)); val == "" {
			break
		}

		if ip := net.ParseIP(val); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, val)
		}
	}

	fmt.Println()

	fmt.Println("How long should the certificate be valid for? A year (365")
	fmt.Println("days) is usual but requires the certificate to be regenerated")
	fmt.Println("within a year or the certificate will cease working.")
	fmt.Println()

	template.NotAfter = template.NotBefore.Add(time.Duration(readNumber("Number of days")) * time.Hour * 24)

	fmt.Println("Common name:", template.Subject.CommonName)
	fmt.Println("DNS SANs:")
	if len(template.DNSNames) == 0 {
		fmt.Println("    None")
	} else {
		for _, e := range template.DNSNames {
			fmt.Println("   ", e)
		}
	}
	fmt.Println("IP SANs:")
	if len(template.IPAddresses) == 0 {
		fmt.Println("    None")
	} else {
		for _, e := range template.IPAddresses {
			fmt.Println("   ", e)
		}
	}
	fmt.Println()

	fmt.Println("The certificate can now be generated")
	fmt.Println("Press any key to begin generating the self-signed certificate.")
	anyKey()

	priv, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		fmt.Println("Failed to generate private key:", err)
		os.Exit(1)
	}

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	template.SerialNumber, err = rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		fmt.Println("Failed to generate serial number:", err)
		os.Exit(1)
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		fmt.Println("Failed to create certificate:", err)
		os.Exit(1)
	}

	certOut, err := os.Create("selfsigned.crt")
	if err != nil {
		fmt.Println("Failed to open selfsigned.pem for writing:", err)
		os.Exit(1)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()

	keyOut, err := os.OpenFile("selfsigned.key", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		fmt.Println("failed to open selfsigned.key for writing:", err)
		os.Exit(1)
	}
	pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
	keyOut.Close()

	fmt.Println("Successfully generated certificate")
	fmt.Println("    Certificate: selfsigned.crt")
	fmt.Println("    Private Key: selfsigned.key")
	fmt.Println()
	fmt.Println("Copy and paste the following into your Log Courier")
	fmt.Println("configuration, adjusting paths as necessary:")
	fmt.Println("    \"transport\": \"tls\",")
	fmt.Println("    \"ssl ca\":    \"path/to/selfsigned.crt\",")
	fmt.Println()
	fmt.Println("Copy and paste the following into your LogStash configuration, ")
	fmt.Println("adjusting paths as necessary:")
	fmt.Println("    ssl_certificate => \"path/to/selfsigned.crt\",")
	fmt.Println("    ssl_key         => \"path/to/selfsigned.key\",")
}