Self-signed certificate authority and generating certificates

Generating a Self-Signed CA and Certificates

I’ve been working with openssh and cfssl to generate a self-signed CA and then generate certificates. It turns out, doing this in Go is simpler than working with the command line tools.

Preamble

We need all the following imports, and then a RandomBigInt function to generate random numbers.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"io/ioutil"
	"math/big"
	"time"
)

func RandomBigInt() *big.Int {
	max := new(big.Int)
	max.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(max, big.NewInt(1))
	n, _ := rand.Int(rand.Reader, max)
	return n
}

CA

This generates a self-signed CA.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func GenerateCA(subject pkix.Name, duryear, durmonth int, rsaKeySize int) (certificate, key *pem.Block, err error) {
	ca := &x509.Certificate{
		SerialNumber:          RandomBigInt(),
		Subject:               subject,
		NotBefore:             time.Now(),
		NotAfter:              time.Now().AddDate(duryear, durmonth, 0),
		IsCA:                  true,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
	}

	priv, _ := rsa.GenerateKey(rand.Reader, rsaKeySize)
	pub := &priv.PublicKey
	var data []byte
	data, err = x509.CreateCertificate(rand.Reader, ca, ca, pub, priv)
	if err != nil {
		return
	}

	// Public key
	certificate = &pem.Block{Type: "CERTIFICATE", Bytes: data}
	// Private key
	key = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}
	return
}

Certificates

This generates a certificate.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func GenerateCertificate(ca *x509.Certificate, caKey crypto.PrivateKey, req x509.CertificateRequest, durYear, durMonth int, keyUsage x509.KeyUsage, extKeyUsage []x509.ExtKeyUsage, rsaKeySize int) (certificate, key *pem.Block, err error) {

	cert := &x509.Certificate{
		Version:         req.Version,
		SerialNumber:    RandomBigInt(),
		Subject:         req.Subject,
		Extensions:      req.Extensions,
		ExtraExtensions: req.ExtraExtensions,
		DNSNames:        req.DNSNames,
		EmailAddresses:  req.EmailAddresses,
		IPAddresses:     req.IPAddresses,
		URIs:            req.URIs,
		NotBefore:       time.Now(),
		NotAfter:        time.Now().AddDate(durYear, durMonth, 0),
		ExtKeyUsage:     extKeyUsage,
		KeyUsage:        keyUsage,
	}
	priv, _ := rsa.GenerateKey(rand.Reader, rsaKeySize)
	pub := &priv.PublicKey

	var data []byte
	data, err = x509.CreateCertificate(rand.Reader, cert, ca, pub, caKey)
	if err != nil {
		return
	}
	// Public key
	certificate = &pem.Block{Type: "CERTIFICATE", Bytes: data}
	// Private key
	key = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}
	return
}

This is how you use it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
name := pkix.Name{
    CommonName: "myCN",
	Organization:  []string{"My Org"},
	Country:       []string{"US"},
	Province:      []string{"CO"},
	Locality:      []string{"Denver"},
	StreetAddress: []string{"123 Main St"},
	PostalCode:    []string{"80000"}}
cert, certKey, err := GenerateCertificate(caCert, key, 
  x509.CertificateRequest{Subject: subject,
		IPAddresses: ipAddresses,
		DNSNames:    dnsNames,
	}, 1, 0, x509.KeyUsageDigitalSignature,
		[]x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, 2048)
        

You can also encode them as PEM:

1
2
3
var certOut, keyOut bytes.Buffer
pem.Encode(&certOut, cert)
pem.Encode(&keyOut, certKey)