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)
|
err = setPrivateKey(args, client)
|
||||||
case `show-private`:
|
case `show-private`:
|
||||||
err = showPrivateKey(args, client)
|
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`:
|
case `create-location`:
|
||||||
err = createLocation(args, client)
|
err = createLocation(args, client)
|
||||||
case `delete-location`:
|
case `delete-location`:
|
||||||
|
@ -60,6 +70,16 @@ func main() {
|
||||||
err = setLocation(args, client)
|
err = setLocation(args, client)
|
||||||
case `show-location`:
|
case `show-location`:
|
||||||
err = showLocation(args, client)
|
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:
|
default:
|
||||||
fmt.Printf("unknown subcommand '%s'\n", cmd)
|
fmt.Printf("unknown subcommand '%s'\n", cmd)
|
||||||
printCommands()
|
printCommands()
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/gibheer/pki"
|
"github.com/gibheer/pki"
|
||||||
|
@ -14,7 +15,6 @@ type (
|
||||||
ID string
|
ID string
|
||||||
|
|
||||||
// The following options are used to generate the content of the CSR.
|
// The following options are used to generate the content of the CSR.
|
||||||
CommonName string
|
|
||||||
DNSNames []string
|
DNSNames []string
|
||||||
EmailAddresses []string
|
EmailAddresses []string
|
||||||
IPAddresses []net.IP
|
IPAddresses []net.IP
|
||||||
|
@ -29,12 +29,11 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewCSR creates a new CSR.
|
// 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) {
|
emailAddresses []string, iPAddresses []net.IP) (*CSR, error) {
|
||||||
return &CSR{
|
return &CSR{
|
||||||
ID: id,
|
ID: id,
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
CommonName: commonName,
|
|
||||||
DNSNames: dnsNames,
|
DNSNames: dnsNames,
|
||||||
EmailAddresses: emailAddresses,
|
EmailAddresses: emailAddresses,
|
||||||
IPAddresses: iPAddresses,
|
IPAddresses: iPAddresses,
|
||||||
|
@ -63,7 +62,6 @@ func (c *CSR) Refresh(lookup *Storage) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
subject := subjRes.GetName()
|
subject := subjRes.GetName()
|
||||||
subject.CommonName = c.CommonName
|
|
||||||
|
|
||||||
opts := pki.CertificateData{
|
opts := pki.CertificateData{
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
|
@ -102,3 +100,113 @@ func (c *CSR) GetCSR() (*pki.CertificateRequest, error) {
|
||||||
}
|
}
|
||||||
return csr, nil
|
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