add csr support
This adds support for certificate sign requsts.
This commit is contained in:
parent
b9456bfd8b
commit
08f39fad0a
|
@ -0,0 +1,130 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/gibheer/pkiadm"
|
||||
"github.com/pkg/errors"
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func createCSR(args []string, client *pkiadm.Client) error {
|
||||
fs := flag.NewFlagSet("create-csr", flag.ExitOnError)
|
||||
fs.Usage = func() {
|
||||
fmt.Printf("Usage of %s:\n", "pkiadm create-csr")
|
||||
fmt.Println(`
|
||||
Create a new certificate sign request. This request can be signed by a CA to create a new certificate.
|
||||
FQDNs, mail addresses and ips can be set multiple times or once as a comma separated list.
|
||||
`)
|
||||
fs.PrintDefaults()
|
||||
}
|
||||
csr := pkiadm.CSR{}
|
||||
fs.StringVar(&csr.ID, "id", "", "set the unique id for the new private key")
|
||||
parseSubject(fs, args, &csr)
|
||||
|
||||
if err := client.CreateCSR(csr); err != nil {
|
||||
return errors.Wrap(err, "could not create private key")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func setCSR(args []string, client *pkiadm.Client) error {
|
||||
fs := flag.NewFlagSet("set-csr", flag.ExitOnError)
|
||||
csr := pkiadm.CSR{}
|
||||
fs.StringVar(&csr.ID, "id", "", "set the id of the CSR to adjust")
|
||||
parseSubject(fs, args, &csr)
|
||||
|
||||
fieldList := []string{}
|
||||
for _, field := range []string{"private-key", "subject", "ip", "fqdn", "mail"} {
|
||||
flag := fs.Lookup(field)
|
||||
if flag.Changed {
|
||||
fieldList = append(fieldList, field)
|
||||
}
|
||||
}
|
||||
|
||||
if err := client.SetCSR(csr, fieldList); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func parseSubject(fs *flag.FlagSet, args []string, csr *pkiadm.CSR) {
|
||||
fs.StringSliceVar(&csr.DNSNames, "fqdn", []string{}, "assign the FQDNs")
|
||||
fs.StringSliceVar(&csr.EmailAddresses, "mail", []string{}, "assign the mail addresses")
|
||||
fs.IPSliceVar(&csr.IPAddresses, "ip", []net.IP{}, "assign the ips")
|
||||
pk := fs.String("private-key", "", "set the id of the private key to sign the request")
|
||||
subject := fs.String("subject", "", "set the id of the subject to use for this request")
|
||||
fs.Parse(args)
|
||||
|
||||
csr.PrivateKey = pkiadm.ResourceName{*pk, pkiadm.RTPrivateKey}
|
||||
csr.Subject = pkiadm.ResourceName{*subject, pkiadm.RTSubject}
|
||||
}
|
||||
|
||||
func deleteCSR(args []string, client *pkiadm.Client) error {
|
||||
fs := flag.NewFlagSet("delete-csr", flag.ExitOnError)
|
||||
var id = fs.String("id", "", "set the id of the csr to delete")
|
||||
fs.Parse(args)
|
||||
|
||||
if err := client.DeleteCSR(*id); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func listCSR(args []string, client *pkiadm.Client) error {
|
||||
fs := flag.NewFlagSet("list-csr", flag.ExitOnError)
|
||||
fs.Parse(args)
|
||||
|
||||
csrs, err := client.ListCSR()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(csrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := tabwriter.NewWriter(os.Stdout, 2, 2, 1, ' ', tabwriter.AlignRight)
|
||||
fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\t\n", "id", "private-key", "subject", "names", "ips", "mails")
|
||||
for _, csr := range csrs {
|
||||
fmt.Fprintf(
|
||||
out,
|
||||
"%s\t%s\t%s\t%d\t%d\t%d\t\n",
|
||||
csr.ID,
|
||||
csr.PrivateKey.ID,
|
||||
csr.Subject.ID,
|
||||
len(csr.DNSNames),
|
||||
len(csr.IPAddresses),
|
||||
len(csr.EmailAddresses),
|
||||
)
|
||||
}
|
||||
out.Flush()
|
||||
|
||||
return nil
|
||||
}
|
||||
func showCSR(args []string, client *pkiadm.Client) error {
|
||||
fs := flag.NewFlagSet("show-private", flag.ExitOnError)
|
||||
var id = fs.String("id", "", "set the id of the private key to show")
|
||||
fs.Parse(args)
|
||||
|
||||
csr, err := client.ShowCSR(*id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ips := []string{}
|
||||
for _, ip := range csr.IPAddresses {
|
||||
ips = append(ips, ip.String())
|
||||
}
|
||||
out := tabwriter.NewWriter(os.Stdout, 2, 2, 1, ' ', tabwriter.AlignRight)
|
||||
fmt.Fprintf(out, "ID:\t%s\t\n", csr.ID)
|
||||
fmt.Fprintf(out, "private:\t%s\t\n", csr.PrivateKey.ID)
|
||||
fmt.Fprintf(out, "subject:\t%s\t\n", csr.Subject.ID)
|
||||
fmt.Fprintf(out, "fqdn:\t%s\t\n", ReplaceEmpty(strings.Join(csr.DNSNames, ", ")))
|
||||
fmt.Fprintf(out, "ip:\t%s\t\n", ReplaceEmpty(strings.Join(ips, ", ")))
|
||||
fmt.Fprintf(out, "mail:\t%s\t\n", ReplaceEmpty(strings.Join(csr.EmailAddresses, ", ")))
|
||||
fmt.Fprintf(out, "checksum:\t%s\t\n", base64.StdEncoding.EncodeToString(csr.Checksum))
|
||||
out.Flush()
|
||||
return nil
|
||||
}
|
|
@ -50,6 +50,16 @@ func main() {
|
|||
err = setPrivateKey(args, client)
|
||||
case `show-private`:
|
||||
err = showPrivateKey(args, client)
|
||||
case `create-public`:
|
||||
err = createPublicKey(args, client)
|
||||
case `delete-public`:
|
||||
err = deletePublicKey(args, client)
|
||||
case `list-public`:
|
||||
err = listPublicKey(args, client)
|
||||
case `set-public`:
|
||||
err = setPublicKey(args, client)
|
||||
case `show-public`:
|
||||
err = showPublicKey(args, client)
|
||||
case `create-location`:
|
||||
err = createLocation(args, client)
|
||||
case `delete-location`:
|
||||
|
@ -60,6 +70,16 @@ func main() {
|
|||
err = setLocation(args, client)
|
||||
case `show-location`:
|
||||
err = showLocation(args, client)
|
||||
case `create-csr`:
|
||||
err = createCSR(args, client)
|
||||
case `delete-csr`:
|
||||
err = deleteCSR(args, client)
|
||||
case `list-csr`:
|
||||
err = listCSR(args, client)
|
||||
case `set-csr`:
|
||||
err = setCSR(args, client)
|
||||
case `show-csr`:
|
||||
err = showCSR(args, client)
|
||||
default:
|
||||
fmt.Printf("unknown subcommand '%s'\n", cmd)
|
||||
printCommands()
|
||||
|
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/gibheer/pki"
|
||||
|
@ -14,7 +15,6 @@ type (
|
|||
ID string
|
||||
|
||||
// The following options are used to generate the content of the CSR.
|
||||
CommonName string
|
||||
DNSNames []string
|
||||
EmailAddresses []string
|
||||
IPAddresses []net.IP
|
||||
|
@ -29,12 +29,11 @@ type (
|
|||
)
|
||||
|
||||
// NewCSR creates a new CSR.
|
||||
func NewCSR(id string, pk, subject pkiadm.ResourceName, commonName string, dnsNames []string,
|
||||
func NewCSR(id string, pk, subject pkiadm.ResourceName, dnsNames []string,
|
||||
emailAddresses []string, iPAddresses []net.IP) (*CSR, error) {
|
||||
return &CSR{
|
||||
ID: id,
|
||||
Subject: subject,
|
||||
CommonName: commonName,
|
||||
DNSNames: dnsNames,
|
||||
EmailAddresses: emailAddresses,
|
||||
IPAddresses: iPAddresses,
|
||||
|
@ -63,7 +62,6 @@ func (c *CSR) Refresh(lookup *Storage) error {
|
|||
return err
|
||||
}
|
||||
subject := subjRes.GetName()
|
||||
subject.CommonName = c.CommonName
|
||||
|
||||
opts := pki.CertificateData{
|
||||
Subject: subject,
|
||||
|
@ -102,3 +100,113 @@ func (c *CSR) GetCSR() (*pki.CertificateRequest, error) {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package pkiadm
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
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 ResourceName
|
||||
Subject ResourceName
|
||||
|
||||
// Checksum provides the checksum of the CSR on the server.
|
||||
Checksum []byte
|
||||
}
|
||||
|
||||
CSRChange struct {
|
||||
CSR CSR
|
||||
FieldList []string
|
||||
}
|
||||
|
||||
ResultCSR struct {
|
||||
Result Result
|
||||
CSRs []CSR
|
||||
}
|
||||
)
|
||||
|
||||
func (c *Client) CreateCSR(pk CSR) error {
|
||||
return c.exec("CreateCSR", pk)
|
||||
}
|
||||
func (c *Client) SetCSR(pk CSR, fieldList []string) error {
|
||||
changeset := CSRChange{pk, fieldList}
|
||||
return c.exec("SetCSR", changeset)
|
||||
}
|
||||
func (c *Client) DeleteCSR(id string) error {
|
||||
pk := ResourceName{ID: id, Type: RTCSR}
|
||||
return c.exec("DeleteCSR", pk)
|
||||
}
|
||||
func (c *Client) ListCSR() ([]CSR, error) {
|
||||
result := &ResultCSR{}
|
||||
if err := c.query("ListCSR", Filter{}, result); err != nil {
|
||||
return []CSR{}, err
|
||||
}
|
||||
if result.Result.HasError {
|
||||
return []CSR{}, result.Result.Error
|
||||
}
|
||||
return result.CSRs, nil
|
||||
}
|
||||
func (c *Client) ShowCSR(id string) (CSR, error) {
|
||||
csr := ResourceName{ID: id, Type: RTCSR}
|
||||
result := &ResultCSR{}
|
||||
if err := c.query("ShowCSR", csr, result); err != nil {
|
||||
return CSR{}, err
|
||||
}
|
||||
if result.Result.HasError {
|
||||
return CSR{}, result.Result.Error
|
||||
}
|
||||
for _, csr := range result.CSRs {
|
||||
return csr, nil
|
||||
}
|
||||
return CSR{}, nil
|
||||
}
|
Loading…
Reference in New Issue