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 +}