diff --git a/pkg/pki/io.go b/pkg/pki/io.go
new file mode 100644
index 0000000000000000000000000000000000000000..319ffa8d61ad209bd5191343918f3b9306aa56fe
--- /dev/null
+++ b/pkg/pki/io.go
@@ -0,0 +1,72 @@
+package pki
+
+import (
+	"crypto/ecdsa"
+	"crypto/x509"
+	"encoding/pem"
+	"errors"
+	"io/ioutil"
+	"os"
+)
+
+func getPem(path string) ([]byte, error) {
+	bytes, err := ioutil.ReadFile(path)
+	if err != nil {
+		return nil, err
+	}
+	decoded, _ := pem.Decode(bytes)
+	if decoded == nil {
+		return nil, errors.New("Could not decode PEM file")
+	}
+	return decoded.Bytes, nil
+}
+
+func setPem(path string, blockType string, bytes []byte) error {
+	block := &pem.Block{
+		Type:  blockType,
+		Bytes: bytes,
+	}
+	file, err := os.Create(path)
+	if err != nil {
+		return err
+	}
+	err = pem.Encode(file, block)
+	if err != nil {
+		return err
+	}
+	return file.Close()
+}
+
+func LoadKey(path string) (*ecdsa.PrivateKey, error) {
+	decoded, err := getPem(path)
+	if os.IsNotExist(err) {
+		return nil, nil
+	}
+	if err != nil {
+		return nil, err
+	}
+	return x509.ParseECPrivateKey(decoded)
+}
+
+func SaveKey(path string, key *ecdsa.PrivateKey) error {
+	keyBytes, err := x509.MarshalECPrivateKey(key)
+	if err != nil {
+		return err
+	}
+	return setPem(path, "ECDSA PRIVATE KEY", keyBytes)
+}
+
+func LoadCert(path string) (*x509.Certificate, error) {
+	decoded, err := getPem(path)
+	if os.IsNotExist(err) {
+		return nil, nil
+	}
+	if err != nil {
+		return nil, err
+	}
+	return x509.ParseCertificate(decoded)
+}
+
+func SaveCert(path string, cert *x509.Certificate) error {
+	return setPem(path, "CERTIFICATE", cert.Raw)
+}
diff --git a/pkg/pki/pki.go b/pkg/pki/pki.go
index 31b74c873c28008f20fcc034907628d34c818b80..5a1b00211ebfa2760136e82199beead2012081da 100644
--- a/pkg/pki/pki.go
+++ b/pkg/pki/pki.go
@@ -6,9 +6,6 @@ import (
 	"crypto/elliptic"
 	"crypto/rand"
 	"crypto/x509"
-	"encoding/pem"
-	"errors"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 )
@@ -19,7 +16,7 @@ const CERT_EXT = ".pem"
 
 type CA struct {
 	path   string
-	signer *crypto.Signer
+	signer crypto.Signer
 	cert   *x509.Certificate
 }
 
@@ -46,36 +43,63 @@ func GetCA(path string) (*CA, error) {
 	}, nil
 }
 
-func getPem(path string) ([]byte, error) {
-	bytes, err := ioutil.ReadFile(path)
+func (c *CA) Sign(csrBytes []byte, template *x509.Certificate) ([]byte, error) {
+	csr, err := x509.ParseCertificateRequest(csrBytes)
 	if err != nil {
 		return nil, err
 	}
-	decoded, _ := pem.Decode(bytes)
-	if decoded == nil {
-		return nil, errors.New("Could not decode PEM file")
+	err = csr.CheckSignature()
+	if err != nil {
+		return nil, err
 	}
-	return decoded.Bytes, nil
+	// Fill the template fields in
+	template.Signature = csr.Signature
+	template.SignatureAlgorithm = csr.SignatureAlgorithm
+	template.PublicKey = csr.PublicKey
+	template.PublicKeyAlgorithm = csr.PublicKeyAlgorithm
+	// Actually sign the certificate
+	return x509.CreateCertificate(rand.Reader, template, c.cert, template.PublicKey, c.signer)
 }
 
-func getSigner(path string) (*crypto.Signer, error) {
-	decoded, err := getPem(path)
-	// Create the private key if necessary
-	if err == nil {
-
-	} else if os.IsExist(err) {
-		signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+func getSigner(path string) (crypto.Signer, error) {
+	key, err := LoadKey(path)
+	if err != nil {
+		return nil, err
+	}
+	// Create the key if necessary
+	if key == nil {
+		key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 		if err != nil {
-			return err
+			return nil, err
 		}
-		err = setPem(path, &pem.Block{
-			Type:  "ECDSA PRIVATE KEY",
-			Bytes: x509.MarshalPKCS1PrivateKey(signer),
-		})
+		err = SaveKey(path, key)
 		if err != nil {
-			return err
+			return nil, err
 		}
-	} else {
+	}
+	return key, nil
+}
+
+func getCaCert(signer crypto.Signer, path string) (*x509.Certificate, error) {
+	cert, err := LoadCert(path)
+	if err != nil {
 		return nil, err
 	}
+	// Self-sign a certificate if required
+	if cert == nil {
+		template := NewCATemplate()
+		certBytes, err := x509.CreateCertificate(rand.Reader, template, template, signer.Public(), signer)
+		if err != nil {
+			return nil, err
+		}
+		cert, err = x509.ParseCertificate(certBytes)
+		if err != nil {
+			return nil, err
+		}
+		err = SaveCert(path, cert)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return cert, nil
 }
diff --git a/pkg/pki/templates.go b/pkg/pki/templates.go
new file mode 100644
index 0000000000000000000000000000000000000000..e82ad26fb97f0731135e27f461e4020fb94fe394
--- /dev/null
+++ b/pkg/pki/templates.go
@@ -0,0 +1,55 @@
+package pki
+
+import (
+	"crypto/rand"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"math/big"
+	"time"
+)
+
+var maxSerial = new(big.Int).Lsh(big.NewInt(1), 128)
+
+func newSerial() *big.Int {
+	serial, err := rand.Int(rand.Reader, maxSerial)
+	if err != nil {
+		panic(err)
+	}
+	return serial
+}
+
+func newTemplate() *x509.Certificate {
+	return &x509.Certificate{
+		SerialNumber:          newSerial(),
+		NotBefore:             time.Now(),
+		NotAfter:              time.Now().Add(time.Hour * 24 * 365 * 10),
+		BasicConstraintsValid: true,
+	}
+}
+
+func NewCATemplate() *x509.Certificate {
+	template := newTemplate()
+	template.Subject = pkix.Name{}
+	template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign
+	template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
+	template.IsCA = true
+	return template
+}
+
+func NewServerTemplate(names []string) *x509.Certificate {
+	template := newTemplate()
+	template.Subject = pkix.Name{
+		CommonName: names[0],
+	}
+	template.DNSNames = names
+	return template
+}
+
+func NewClientTemplate(cn string, org string) *x509.Certificate {
+	template := newTemplate()
+	template.Subject = pkix.Name{
+		CommonName:   cn,
+		Organization: []string{org},
+	}
+	return template
+}