From bc8e1353afe154763bb023dfe13cd3d3192e89fb Mon Sep 17 00:00:00 2001 From: Gibheer Date: Sun, 28 May 2017 23:55:39 +0200 Subject: [PATCH] add private key handling This also contains a couple fixes to the subject part. --- cmd/pkiadm/main.go | 32 +++------ cmd/pkiadm/private_key.go | 116 +++++++++++++++++++++++++++++++ cmd/pkiadm/subject.go | 1 - cmd/pkiadmd/private_key.go | 137 +++++++++++++++++++++++++++++-------- cmd/pkiadmd/subject.go | 6 +- private_key.go | 63 +++++++++++++++++ privatekeytype_string.go | 29 ++++++++ subject.go | 2 +- 8 files changed, 331 insertions(+), 55 deletions(-) create mode 100644 cmd/pkiadm/private_key.go create mode 100644 private_key.go create mode 100644 privatekeytype_string.go diff --git a/cmd/pkiadm/main.go b/cmd/pkiadm/main.go index 4faaae0..9eeae99 100644 --- a/cmd/pkiadm/main.go +++ b/cmd/pkiadm/main.go @@ -40,28 +40,16 @@ func main() { err = setSubject(args, client) case `show-subj`: err = showSubject(args, client) - // case `list`: - // err = listDescription(args, client) - // case `create-file`: - // err = createFile(args, client) - // case `list-files`: - // err = listFile(args, client) - // case `delete-file`: - // err = deleteFile(args, client) - // case `create-private-key`: - // err = createPrivateKey(args, client) - // case `get-private-key`: - // err = getPrivateKey(args, client) - // case `list-private-keys`: - // err = listPrivateKey(args, client) - // case `delete-private-key`: - // err = deletePrivateKey(args, client) - // case `create-public-key`: - // err = createPublicKey(args, client) - // case `list-public-keys`: - // err = listPublicKey(args, client) - // case `delete-public-key`: - // err = deletePublicKey(args, client) + case `create-private`: + err = createPrivateKey(args, client) + case `delete-private`: + err = deletePrivateKey(args, client) + case `list-private`: + err = listPrivateKey(args, client) + case `set-private`: + err = setPrivateKey(args, client) + case `show-private`: + err = showPrivateKey(args, client) default: fmt.Printf("unknown subcommand '%s'\n", cmd) printCommands() diff --git a/cmd/pkiadm/private_key.go b/cmd/pkiadm/private_key.go new file mode 100644 index 0000000..e64dd71 --- /dev/null +++ b/cmd/pkiadm/private_key.go @@ -0,0 +1,116 @@ +package main + +import ( + "encoding/base64" + "fmt" + "os" + "text/tabwriter" + + "github.com/gibheer/pkiadm" + "github.com/pkg/errors" + flag "github.com/spf13/pflag" +) + +func createPrivateKey(args []string, client *pkiadm.Client) error { + fs := flag.NewFlagSet("create-private", flag.ExitOnError) + fs.Usage = func() { + fmt.Printf("Usage of %s:\n", "pkiadm create-private") + fmt.Println(` +Create a new private key for different use cases. The supported types are rsa, +ecdsa and ed25519. Please keep in mind, that ed25519 is currently not supported +for certificate generation. +`) + fs.PrintDefaults() + } + pk := pkiadm.PrivateKey{} + fs.StringVar(&pk.ID, "id", "", "set the unique id for the new private key") + var pkType = fs.String("type", "rsa", "set the type of the private key (rsa, ecdsa, ed25519)") + fs.UintVar(&pk.Bits, "bits", 2048, "set the number of bits to use. For rsa it can be 1024 up to 32768, for ecdsa 224, 256, 384, 521. Ed25519 is set to 256 by default.") + fs.Parse(args) + + pkT, err := pkiadm.StringToPrivateKeyType(*pkType) + if err != nil { + return err + } + pk.Type = pkT + if err := client.CreatePrivateKey(pk); err != nil { + return errors.Wrap(err, "could not create private key") + } + + return nil +} +func setPrivateKey(args []string, client *pkiadm.Client) error { + fs := flag.NewFlagSet("set-private", flag.ExitOnError) + pk := pkiadm.PrivateKey{} + fs.StringVar(&pk.ID, "id", "", "set the id of the private key to change") + var pkType = fs.String("type", "rsa", "set the type of the private key (rsa, ecdsa, ed25519)") + fs.UintVar(&pk.Bits, "bits", 2048, "set the number of bits to use. For rsa it can be 1024 up to 32768, for ecdsa 224, 256, 384, 521. Ed25519 is set to 256 by default.") + fs.Parse(args) + + pkT, err := pkiadm.StringToPrivateKeyType(*pkType) + if err != nil { + return err + } + pk.Type = pkT + + fieldList := []string{} + for _, field := range []string{"type", "bits"} { + flag := fs.Lookup(field) + if flag.Changed { + fieldList = append(fieldList, field) + } + } + + if err := client.SetPrivateKey(pk, fieldList); err != nil { + return err + } + return nil +} +func deletePrivateKey(args []string, client *pkiadm.Client) error { + fs := flag.NewFlagSet("delete-private", flag.ExitOnError) + var id = fs.String("id", "", "set the id of the private key to delete") + fs.Parse(args) + + if err := client.DeletePrivateKey(*id); err != nil { + return err + } + return nil +} +func listPrivateKey(args []string, client *pkiadm.Client) error { + fs := flag.NewFlagSet("list-private", flag.ExitOnError) + fs.Parse(args) + + pks, err := client.ListPrivateKey() + if err != nil { + return err + } + + if len(pks) == 0 { + return nil + } + out := tabwriter.NewWriter(os.Stdout, 2, 2, 1, ' ', tabwriter.AlignRight) + fmt.Fprintf(out, "%s\t%s\t%s\t\n", "id", "type", "bits") + for _, pk := range pks { + fmt.Fprintf(out, "%s\t%s\t%d\t\n", pk.ID, pk.Type.String(), pk.Bits) + } + out.Flush() + + return nil +} +func showPrivateKey(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) + + pk, err := client.ShowPrivateKey(*id) + if err != nil { + return err + } + out := tabwriter.NewWriter(os.Stdout, 2, 2, 1, ' ', tabwriter.AlignRight) + fmt.Fprintf(out, "ID:\t%s\t\n", pk.ID) + fmt.Fprintf(out, "type:\t%s\t\n", pk.Type.String()) + fmt.Fprintf(out, "bits:\t%d\t\n", pk.Bits) + fmt.Fprintf(out, "checksum:\t%s\t\n", base64.StdEncoding.EncodeToString(pk.Checksum)) + out.Flush() + return nil +} diff --git a/cmd/pkiadm/subject.go b/cmd/pkiadm/subject.go index 91a63b2..7d457d6 100644 --- a/cmd/pkiadm/subject.go +++ b/cmd/pkiadm/subject.go @@ -32,7 +32,6 @@ In most cases only the common name, organization name and the country is provide } if err := client.CreateSubject(subj); err != nil { - fmt.Println("got an error") return errors.Wrap(err, "could not create new subject") } return nil diff --git a/cmd/pkiadmd/private_key.go b/cmd/pkiadmd/private_key.go index fffe8ff..7f4392e 100644 --- a/cmd/pkiadmd/private_key.go +++ b/cmd/pkiadmd/private_key.go @@ -6,12 +6,7 @@ import ( "fmt" "github.com/gibheer/pki" -) - -const ( - PKTRSA PrivateKeyType = iota - PKTECDSA - PKTED25519 + "github.com/gibheer/pkiadm" ) const ( @@ -23,24 +18,23 @@ const ( type ( PrivateKey struct { ID string - PKType PrivateKeyType - Length uint + PKType pkiadm.PrivateKeyType + Bits uint Key []byte } - PrivateKeyType uint ) -func NewPrivateKey(id string, pkType PrivateKeyType, length uint) (*PrivateKey, error) { +func NewPrivateKey(id string, pkType pkiadm.PrivateKeyType, bits uint) (*PrivateKey, error) { if id == "" { return nil, ENoIDGiven } - if err := verifyPK(pkType, length); err != nil { + if err := verifyPK(pkType, bits); err != nil { return nil, err } pk := PrivateKey{ ID: id, PKType: pkType, - Length: length, + Bits: bits, } return &pk, nil } @@ -67,13 +61,13 @@ func (p *PrivateKey) Refresh(_ *Storage) error { err error ) switch p.PKType { - case PKTRSA: - key, err = pki.NewPrivateKeyRsa(int(p.Length)) - case PKTED25519: + case pkiadm.PKTRSA: + key, err = pki.NewPrivateKeyRsa(int(p.Bits)) + case pkiadm.PKTED25519: key, err = pki.NewPrivateKeyEd25519() - case PKTECDSA: + case pkiadm.PKTECDSA: var curve elliptic.Curve - switch p.Length { + switch p.Bits { case 224: curve = elliptic.P224() case 256: @@ -120,20 +114,20 @@ func (p *PrivateKey) GetKey() (pki.PrivateKey, error) { return key, nil } -func verifyPK(pkType PrivateKeyType, length uint) error { +func verifyPK(pkType pkiadm.PrivateKeyType, bits uint) error { switch pkType { - case PKTRSA: - if length < 1024 || length > 32768 { + case pkiadm.PKTRSA: + if bits < 1024 || bits > 32768 { return ELengthOutOfBounds } - case PKTECDSA: - switch length { + case pkiadm.PKTECDSA: + switch bits { case 224, 256, 384, 521: default: return EWrongKeyLength } - case PKTED25519: - if length != 256 { + case pkiadm.PKTED25519: + if bits != 256 { return EWrongKeyLengthED25519 } default: @@ -142,9 +136,92 @@ func verifyPK(pkType PrivateKeyType, length uint) error { return nil } -//func (p *PrivateKey) MarshalJSON() ([]byte, error) { -// return json.Marshal(*p) -//} -//func (p *PrivateKey) UnmarshalJSON(raw []byte) error { -// return json.Unmarshal(raw, p) -//} +func (s *Server) CreatePrivateKey(inPk pkiadm.PrivateKey, res *pkiadm.Result) error { + s.lock() + defer s.unlock() + + pk, err := NewPrivateKey(inPk.ID, inPk.Type, inPk.Bits) + if err != nil { + res.SetError(err, "Could not create new private key '%s'", inPk.ID) + return nil + } + if err := s.storage.AddPrivateKey(pk); err != nil { + res.SetError(err, "Could not add private key '%s'", inPk.ID) + return nil + } + return nil +} +func (s *Server) SetPrivateKey(changeset pkiadm.PrivateKeyChange, res *pkiadm.Result) error { + s.lock() + defer s.unlock() + + pk, err := s.storage.GetPrivateKey(ResourceName{ID: changeset.PrivateKey.ID, Type: RTPrivateKey}) + if err != nil { + res.SetError(err, "Could not find private key '%s'", changeset.PrivateKey.ID) + return nil + } + + for _, field := range changeset.FieldList { + switch field { + case "type": + pk.PKType = changeset.PrivateKey.Type + case "bits": + pk.Bits = changeset.PrivateKey.Bits + default: + res.SetError(fmt.Errorf("unknown field"), "unknown field '%s'", field) + return nil + } + } + if err := s.storage.Update(ResourceName{ID: pk.ID, Type: RTPrivateKey}); err != nil { + res.SetError(err, "Could not update private key '%s'", changeset.PrivateKey.ID) + return nil + } + return s.store(res) +} +func (s *Server) DeletePrivateKey(inPk pkiadm.ResourceName, res *pkiadm.Result) error { + s.lock() + defer s.unlock() + + pk, err := s.storage.GetPrivateKey(ResourceName{ID: inPk.ID, Type: RTPrivateKey}) + if err != nil { + res.SetError(err, "Could not find private key '%s'", inPk.ID) + return nil + } + + if err := s.storage.Remove(pk); err != nil { + res.SetError(err, "Could not remove private key '%s'", pk.ID) + return nil + } + return s.store(res) +} +func (s *Server) ShowPrivateKey(inPk pkiadm.ResourceName, res *pkiadm.ResultPrivateKey) error { + s.lock() + defer s.unlock() + + pk, err := s.storage.GetPrivateKey(ResourceName{ID: inPk.ID, Type: RTPrivateKey}) + if err != nil { + res.Result.SetError(err, "Could not find private key '%s'", inPk.ID) + return nil + } + res.PrivateKeys = []pkiadm.PrivateKey{pkiadm.PrivateKey{ + ID: pk.ID, + Type: pk.PKType, + Bits: pk.Bits, + Checksum: pk.Checksum(), + }} + return nil +} +func (s *Server) ListPrivateKey(filter pkiadm.Filter, res *pkiadm.ResultPrivateKey) error { + s.lock() + defer s.unlock() + + for _, pk := range s.storage.PrivateKeys { + res.PrivateKeys = append(res.PrivateKeys, pkiadm.PrivateKey{ + ID: pk.ID, + Type: pk.PKType, + Bits: pk.Bits, + Checksum: pk.Checksum(), + }) + } + return nil +} diff --git a/cmd/pkiadmd/subject.go b/cmd/pkiadmd/subject.go index 9bb55f5..a4879df 100644 --- a/cmd/pkiadmd/subject.go +++ b/cmd/pkiadmd/subject.go @@ -2,6 +2,7 @@ package main import ( "crypto/x509/pkix" + "fmt" "github.com/gibheer/pkiadm" ) @@ -90,10 +91,13 @@ func (s *Server) SetSubject(changeset pkiadm.SubjectChange, res *pkiadm.Result) subj.Data.StreetAddress = changes.StreetAddress case "code": subj.Data.PostalCode = changes.PostalCode + default: + res.SetError(fmt.Errorf("unknown field"), "unknown field '%s'", field) + return nil } } if err := s.storage.Update(ResourceName{ID: subj.ID, Type: RTSubject}); err != nil { - res.SetError(err, "Could update resource '%s'", changeset.Subject.ID) + res.SetError(err, "Could not update subject '%s'", changeset.Subject.ID) return nil } return s.store(res) diff --git a/private_key.go b/private_key.go new file mode 100644 index 0000000..56b8ab8 --- /dev/null +++ b/private_key.go @@ -0,0 +1,63 @@ +package pkiadm + +const ( + PKTRSA PrivateKeyType = iota + PKTECDSA + PKTED25519 + PKTUnknown +) + +type ( + PrivateKey struct { + ID string + Type PrivateKeyType + Bits uint + Checksum []byte // This field is only set by the server + } + PrivateKeyChange struct { + PrivateKey PrivateKey + FieldList []string + } + ResultPrivateKey struct { + Result Result + PrivateKeys []PrivateKey + } + PrivateKeyType uint +) + +// CreatePrivateKey sends a RPC request to create a new private key. +func (c *Client) CreatePrivateKey(pk PrivateKey) error { + return c.exec("CreatePrivateKey", pk) +} +func (c *Client) SetPrivateKey(pk PrivateKey, fieldList []string) error { + changeset := PrivateKeyChange{pk, fieldList} + return c.exec("SetPrivateKey", changeset) +} +func (c *Client) DeletePrivateKey(id string) error { + pk := ResourceName{ID: id, Type: RTPrivateKey} + return c.exec("DeletePrivateKey", pk) +} +func (c *Client) ListPrivateKey() ([]PrivateKey, error) { + result := &ResultPrivateKey{} + if err := c.query("ListPrivateKey", Filter{}, result); err != nil { + return []PrivateKey{}, err + } + if result.Result.HasError { + return []PrivateKey{}, result.Result.Error + } + return result.PrivateKeys, nil +} +func (c *Client) ShowPrivateKey(id string) (PrivateKey, error) { + pk := ResourceName{ID: id, Type: RTPrivateKey} + result := &ResultPrivateKey{} + if err := c.query("ShowPrivateKey", pk, result); err != nil { + return PrivateKey{}, err + } + if result.Result.HasError { + return PrivateKey{}, result.Result.Error + } + for _, privateKey := range result.PrivateKeys { + return privateKey, nil + } + return PrivateKey{}, nil +} diff --git a/privatekeytype_string.go b/privatekeytype_string.go new file mode 100644 index 0000000..82c9435 --- /dev/null +++ b/privatekeytype_string.go @@ -0,0 +1,29 @@ +// Code generated by "stringer -type PrivateKeyType"; DO NOT EDIT + +package pkiadm + +import "fmt" + +const _PrivateKeyType_name = "rsaecdsaed25519" + +var _PrivateKeyType_index = [...]uint8{0, 3, 8, 15} + +func (i PrivateKeyType) String() string { + if i >= PrivateKeyType(len(_PrivateKeyType_index)-1) { + return fmt.Sprintf("PrivateKeyType(%d)", i) + } + return _PrivateKeyType_name[_PrivateKeyType_index[i]:_PrivateKeyType_index[i+1]] +} + +func StringToPrivateKeyType(t string) (PrivateKeyType, error) { + switch t { + case "rsa": + return PKTRSA, nil + case "ecdsa": + return PKTECDSA, nil + case "ed25519": + return PKTED25519, nil + default: + return PKTUnknown, fmt.Errorf("unknown private key type") + } +} diff --git a/subject.go b/subject.go index ef7143e..30b38f2 100644 --- a/subject.go +++ b/subject.go @@ -39,7 +39,7 @@ func (c *Client) ShowSubject(id string) (Subject, error) { subj := ResourceName{ID: id, Type: RTSubject} result := &ResultSubjects{} if err := c.query("ShowSubject", subj, result); err != nil { - return Subject{}, nil + return Subject{}, err } if result.Result.HasError { return Subject{}, result.Result.Error