package main import ( "encoding/pem" "fmt" "net" "github.com/gibheer/pki" "github.com/gibheer/pkiadm" ) type ( CSR struct { // ID is the unique identifier of the CSR. ID string // The following options are used to generate the content of the CSR. DNSNames []string EmailAddresses []string IPAddresses []net.IP // PrivateKey is needed to sign the certificate sign request. PrivateKey pkiadm.ResourceName Subject pkiadm.ResourceName // Data contains the pem representation of the CSR. Data []byte } ) // NewCSR creates a new CSR. func NewCSR(id string, pk, subject pkiadm.ResourceName, dnsNames []string, emailAddresses []string, iPAddresses []net.IP) (*CSR, error) { return &CSR{ ID: id, Subject: subject, DNSNames: dnsNames, EmailAddresses: emailAddresses, IPAddresses: iPAddresses, PrivateKey: pk, }, nil } // Return the unique ResourceName func (c *CSR) Name() pkiadm.ResourceName { return pkiadm.ResourceName{c.ID, pkiadm.RTCSR} } // AddDependency registers a depending resource to be retuened by Dependencies() // Refresh must trigger a rebuild of the resource. func (c *CSR) Refresh(lookup *Storage) error { pk, err := lookup.GetPrivateKey(c.PrivateKey) if err != nil { return err } key, err := pk.GetKey() if err != nil { return err } subjRes, err := lookup.GetSubject(c.Subject) if err != nil { return err } subject := subjRes.GetName() opts := pki.CertificateData{ Subject: subject, DNSNames: c.DNSNames, EmailAddresses: c.EmailAddresses, IPAddresses: c.IPAddresses, } csr, err := opts.ToCertificateRequest(key) if err != nil { return err } block, err := csr.ToPem() if err != nil { return err } block.Headers = map[string]string{"ID": c.ID} c.Data = pem.EncodeToMemory(&block) return nil } // Return the PEM output of the contained resource. func (c *CSR) Pem() ([]byte, error) { return c.Data, nil } func (c *CSR) Checksum() []byte { return Hash(c.Data) } // DependsOn must return the resource names it is depending on. func (c *CSR) DependsOn() []pkiadm.ResourceName { return []pkiadm.ResourceName{c.PrivateKey} } func (c *CSR) GetCSR() (*pki.CertificateRequest, error) { // TODO fix this, we must check if there is anything else block, _ := pem.Decode(c.Data) csr, err := pki.LoadCertificateSignRequest(block.Bytes) if err != nil { return nil, err } return csr, nil } func (s *Server) CreateCSR(inCSR pkiadm.CSR, res *pkiadm.Result) error { s.lock() defer s.unlock() csr, err := NewCSR( inCSR.ID, inCSR.PrivateKey, inCSR.Subject, inCSR.DNSNames, inCSR.EmailAddresses, inCSR.IPAddresses, ) if err != nil { res.SetError(err, "Could not create new private key '%s'", inCSR.ID) return nil } if err := s.storage.AddCSR(csr); err != nil { res.SetError(err, "Could not add private key '%s'", inCSR.ID) return nil } return s.store(res) } func (s *Server) SetCSR(changeset pkiadm.CSRChange, res *pkiadm.Result) error { s.lock() defer s.unlock() csr, err := s.storage.GetCSR(pkiadm.ResourceName{ID: changeset.CSR.ID, Type: pkiadm.RTCSR}) if err != nil { res.SetError(err, "Could not find private key '%s'", changeset.CSR.ID) return nil } change := changeset.CSR for _, field := range changeset.FieldList { switch field { case "private-key": csr.PrivateKey = change.PrivateKey case "subject": csr.Subject = change.Subject case "ip": csr.IPAddresses = change.IPAddresses case "fqdn": csr.DNSNames = change.DNSNames case "mail": csr.EmailAddresses = change.EmailAddresses default: res.SetError(fmt.Errorf("unknown field"), "unknown field '%s'", field) return nil } } if err := s.storage.Update(pkiadm.ResourceName{ID: csr.ID, Type: pkiadm.RTCSR}); err != nil { res.SetError(err, "Could not update private key '%s'", changeset.CSR.ID) return nil } return s.store(res) } func (s *Server) DeleteCSR(inCSR pkiadm.ResourceName, res *pkiadm.Result) error { s.lock() defer s.unlock() csr, err := s.storage.GetCSR(pkiadm.ResourceName{ID: inCSR.ID, Type: pkiadm.RTCSR}) if err != nil { res.SetError(err, "Could not find private key '%s'", inCSR.ID) return nil } if err := s.storage.Remove(csr); err != nil { res.SetError(err, "Could not remove private key '%s'", csr.ID) return nil } return s.store(res) } func (s *Server) ShowCSR(inCSR pkiadm.ResourceName, res *pkiadm.ResultCSR) error { s.lock() defer s.unlock() csr, err := s.storage.GetCSR(pkiadm.ResourceName{ID: inCSR.ID, Type: pkiadm.RTCSR}) if err != nil { res.Result.SetError(err, "Could not find private key '%s'", inCSR.ID) return nil } res.CSRs = []pkiadm.CSR{pkiadm.CSR{ ID: csr.ID, Subject: csr.Subject, PrivateKey: csr.PrivateKey, EmailAddresses: csr.EmailAddresses, DNSNames: csr.DNSNames, IPAddresses: csr.IPAddresses, Checksum: csr.Checksum(), }} return nil } func (s *Server) ListCSR(filter pkiadm.Filter, res *pkiadm.ResultCSR) error { s.lock() defer s.unlock() for _, csr := range s.storage.CSRs { res.CSRs = append(res.CSRs, pkiadm.CSR{ ID: csr.ID, Subject: csr.Subject, PrivateKey: csr.PrivateKey, EmailAddresses: csr.EmailAddresses, DNSNames: csr.DNSNames, IPAddresses: csr.IPAddresses, Checksum: csr.Checksum(), }) } return nil }