pkiadm/cmd/pkiadmd/certificate.go

254 lines
6.0 KiB
Go

package main
import (
"encoding/pem"
"fmt"
"time"
"github.com/gibheer/pki"
"github.com/gibheer/pkiadm"
)
type (
Signer interface {
Sign(*CSR) (pki.Certificate, error)
}
Certificate struct {
ID string
IsCA bool
Interval Interval
// TODO remove obsolete field - got replaced with interval
Duration time.Duration
Created time.Time
PrivateKey pkiadm.ResourceName
Serial pkiadm.ResourceName
CSR pkiadm.ResourceName
CA pkiadm.ResourceName
Data []byte
}
)
func NewCertificate(id string, privateKey, serial, csr, ca pkiadm.ResourceName, selfSign bool, duration time.Duration) (*Certificate, error) {
if id == "" {
return nil, ENoIDGiven
}
return &Certificate{
ID: id,
PrivateKey: privateKey,
Serial: serial,
CSR: csr,
CA: ca,
IsCA: selfSign,
Duration: duration,
Interval: Interval{
Created: time.Now(),
RefreshAfter: duration,
},
}, nil
}
// Return the unique ResourceName
func (c *Certificate) Name() pkiadm.ResourceName {
return pkiadm.ResourceName{c.ID, pkiadm.RTCertificate}
}
// AddDependency registers a depending resource to be retuened by Dependencies()
// Refresh must trigger a rebuild of the resource.
func (c *Certificate) Refresh(lookup *Storage) error {
var err error
ca := CASelfSign
if !c.IsCA {
ca, err = lookup.GetCA(c.CA)
if err != nil {
return err
}
}
serRes, err := lookup.GetSerial(c.Serial)
if err != nil {
return err
}
serial, err := serRes.Generate()
if err != nil {
return err
}
// now we can start with the real interesting stuff
// TODO add key usage and that stuff
opts := pki.CertificateOptions{
SerialNumber: serial,
NotBefore: time.Now(),
NotAfter: time.Now().Add(c.Duration),
IsCA: c.IsCA,
CALength: 0, // TODO make this an option
}
//cert, err := csr.ToCertificate(pk, opts, ca)
cert, err := ca.Sign(lookup, c.CSR, opts)
if err != nil {
return err
}
block, err := cert.ToPem()
if err != nil {
return err
}
c.Data = pem.EncodeToMemory(&block)
// TODO remove obsolete field
c.Created = time.Now()
c.Interval.LastRefresh = time.Now()
return nil
}
func (c *Certificate) RefreshInterval() Interval {
return c.Interval
}
func (c *Certificate) GetCertificate() (*pki.Certificate, error) {
// TODO fix this, we must check if there is anything else
block, _ := pem.Decode(c.Data)
cert, err := pki.LoadCertificate(block.Bytes)
if err != nil {
return nil, err
}
return cert, nil
}
// Return the PEM output of the contained resource.
func (c *Certificate) Pem() ([]byte, error) { return c.Data, nil }
func (c *Certificate) Checksum() []byte { return Hash(c.Data) }
// DependsOn must return the resource names it is depending on.
func (c *Certificate) DependsOn() []pkiadm.ResourceName {
res := []pkiadm.ResourceName{
c.PrivateKey,
c.Serial,
c.CSR,
}
if !c.IsCA {
res = append(res, c.CA)
}
return res
}
func (s *Server) CreateCertificate(inCert pkiadm.Certificate, res *pkiadm.Result) error {
s.lock()
defer s.unlock()
cert, err := NewCertificate(
inCert.ID,
inCert.PrivateKey,
inCert.Serial,
inCert.CSR,
inCert.CA,
inCert.IsCA,
inCert.Duration,
)
if err != nil {
res.SetError(err, "Could not create new certificate '%s'", inCert.ID)
return nil
}
if err := s.storage.AddCertificate(cert); err != nil {
res.SetError(err, "Could not add certificate '%s'", inCert.ID)
return nil
}
return s.store(res)
}
func (s *Server) SetCertificate(changeset pkiadm.CertificateChange, res *pkiadm.Result) error {
s.lock()
defer s.unlock()
cert, err := s.storage.GetCertificate(pkiadm.ResourceName{ID: changeset.Certificate.ID, Type: pkiadm.RTCertificate})
if err != nil {
res.SetError(err, "Could not find certficate '%s'", changeset.Certificate.ID)
return nil
}
change := changeset.Certificate
for _, field := range changeset.FieldList {
switch field {
case "duration":
cert.Duration = change.Duration
cert.Interval.RefreshAfter = change.Duration
case "private":
cert.PrivateKey = change.PrivateKey
case "csr":
cert.CSR = change.CSR
case "serial":
cert.Serial = change.Serial
case "ca":
cert.CA = change.CA
case "self-sign":
cert.IsCA = change.IsCA
default:
res.SetError(fmt.Errorf("unknown field"), "unknown field '%s'", field)
return nil
}
}
if err := s.storage.Update(cert.Name()); err != nil {
res.SetError(err, "Could not update certificate '%s'", changeset.Certificate.ID)
return nil
}
return s.store(res)
}
func (s *Server) DeleteCertificate(inCert pkiadm.ResourceName, res *pkiadm.Result) error {
s.lock()
defer s.unlock()
cert, err := s.storage.GetCertificate(pkiadm.ResourceName{ID: inCert.ID, Type: pkiadm.RTCertificate})
if err != nil {
res.SetError(err, "Could not find certificate '%s'", inCert.ID)
return nil
}
if err := s.storage.Remove(cert); err != nil {
res.SetError(err, "Could not remove certificate '%s'", cert.ID)
return nil
}
return s.store(res)
}
func (s *Server) ShowCertificate(inCert pkiadm.ResourceName, res *pkiadm.ResultCertificate) error {
s.lock()
defer s.unlock()
cert, err := s.storage.GetCertificate(pkiadm.ResourceName{ID: inCert.ID, Type: pkiadm.RTCertificate})
if err != nil {
res.Result.SetError(err, "Could not find certificate '%s'", inCert.ID)
return nil
}
res.Certificates = []pkiadm.Certificate{pkiadm.Certificate{
ID: cert.ID,
Duration: cert.Duration,
Created: cert.Created,
PrivateKey: cert.PrivateKey,
Serial: cert.Serial,
CA: cert.CA,
CSR: cert.CSR,
Checksum: cert.Checksum(),
}}
return nil
}
func (s *Server) ListCertificate(filter pkiadm.Filter, res *pkiadm.ResultCertificate) error {
s.lock()
defer s.unlock()
for _, cert := range s.storage.Certificates {
res.Certificates = append(res.Certificates, pkiadm.Certificate{
ID: cert.ID,
Duration: cert.Duration,
Created: cert.Created,
PrivateKey: cert.PrivateKey,
Serial: cert.Serial,
CA: cert.CA,
CSR: cert.CSR,
Checksum: cert.Checksum(),
})
}
return nil
}