From 9e1c6183111ec58f00ab987b0db2bfc897c4dd2b Mon Sep 17 00:00:00 2001 From: Pierre-Henri Symoneaux Date: Mon, 3 Feb 2025 09:14:59 +0100 Subject: [PATCH] feat: add KMIP commands Signed-off-by: Pierre-Henri Symoneaux --- .github/workflows/test.yaml | 6 + README.md | 23 +- cmd/okms/configure/root.go | 16 +- cmd/okms/kmip/attributes.go | 77 ++++ cmd/okms/kmip/create.go | 159 +++++++ cmd/okms/kmip/get.go | 53 +++ cmd/okms/kmip/locate.go | 95 +++++ cmd/okms/kmip/register.go | 387 ++++++++++++++++++ cmd/okms/kmip/rekey.go | 64 +++ cmd/okms/kmip/root.go | 151 +++++++ cmd/okms/main.go | 2 + cmd/okms/okms.sample.yaml | 9 +- common/flagsmgmt/kmipflags/algorithms.go | 69 ++++ common/flagsmgmt/kmipflags/curve.go | 48 +++ common/flagsmgmt/kmipflags/key_usage.go | 103 +++++ common/flagsmgmt/kmipflags/object_type.go | 61 +++ .../flagsmgmt/kmipflags/revocation_reason.go | 51 +++ common/flagsmgmt/kmipflags/state.go | 51 +++ common/flagsmgmt/restflags/key_usage.go | 12 +- doc/okms.md | 1 + doc/okms_kmip.md | 37 ++ doc/okms_kmip_activate.md | 32 ++ doc/okms_kmip_attributes.md | 29 ++ doc/okms_kmip_attributes_get.md | 32 ++ doc/okms_kmip_create.md | 30 ++ doc/okms_kmip_create_key-pair.md | 43 ++ doc/okms_kmip_create_symmetric.md | 40 ++ doc/okms_kmip_destroy.md | 33 ++ doc/okms_kmip_get.md | 32 ++ doc/okms_kmip_locate.md | 35 ++ doc/okms_kmip_register.md | 34 ++ doc/okms_kmip_register_certificate.md | 42 ++ doc/okms_kmip_register_key-pair.md | 46 +++ doc/okms_kmip_register_private-key.md | 45 ++ doc/okms_kmip_register_public-key.md | 43 ++ doc/okms_kmip_register_secret.md | 42 ++ doc/okms_kmip_register_symmetric.md | 46 +++ doc/okms_kmip_rekey.md | 33 ++ doc/okms_kmip_revoke.md | 35 ++ go.mod | 1 + go.sum | 2 + tests/kmip.yaml | 326 +++++++++++++++ tests/testdata/ecdsa_x509.pub.pem | 4 + tests/testdata/rsa_x509.pub.pem | 9 + tests/testdata/x509_cert.pem | 13 + 45 files changed, 2489 insertions(+), 13 deletions(-) create mode 100644 cmd/okms/kmip/attributes.go create mode 100644 cmd/okms/kmip/create.go create mode 100644 cmd/okms/kmip/get.go create mode 100644 cmd/okms/kmip/locate.go create mode 100644 cmd/okms/kmip/register.go create mode 100644 cmd/okms/kmip/rekey.go create mode 100644 cmd/okms/kmip/root.go create mode 100644 common/flagsmgmt/kmipflags/algorithms.go create mode 100644 common/flagsmgmt/kmipflags/curve.go create mode 100644 common/flagsmgmt/kmipflags/key_usage.go create mode 100644 common/flagsmgmt/kmipflags/object_type.go create mode 100644 common/flagsmgmt/kmipflags/revocation_reason.go create mode 100644 common/flagsmgmt/kmipflags/state.go create mode 100644 doc/okms_kmip.md create mode 100644 doc/okms_kmip_activate.md create mode 100644 doc/okms_kmip_attributes.md create mode 100644 doc/okms_kmip_attributes_get.md create mode 100644 doc/okms_kmip_create.md create mode 100644 doc/okms_kmip_create_key-pair.md create mode 100644 doc/okms_kmip_create_symmetric.md create mode 100644 doc/okms_kmip_destroy.md create mode 100644 doc/okms_kmip_get.md create mode 100644 doc/okms_kmip_locate.md create mode 100644 doc/okms_kmip_register.md create mode 100644 doc/okms_kmip_register_certificate.md create mode 100644 doc/okms_kmip_register_key-pair.md create mode 100644 doc/okms_kmip_register_private-key.md create mode 100644 doc/okms_kmip_register_public-key.md create mode 100644 doc/okms_kmip_register_secret.md create mode 100644 doc/okms_kmip_register_symmetric.md create mode 100644 doc/okms_kmip_rekey.md create mode 100644 doc/okms_kmip_revoke.md create mode 100644 tests/kmip.yaml create mode 100644 tests/testdata/ecdsa_x509.pub.pem create mode 100644 tests/testdata/rsa_x509.pub.pem create mode 100644 tests/testdata/x509_cert.pem diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index baf11e2..a6671c7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -33,6 +33,12 @@ jobs: type: mtls cert: $(pwd)/tls.crt key: $(pwd)/tls.key + kmip: + endpoint: ${{secrets.KMS_KMIP_ENDPOINT}} + auth: + type: mtls + cert: $(pwd)/tls.crt + key: $(pwd)/tls.key EOF - name: Test connectivity to KMS dmain run: ./okms keys ls -d -c okms.yaml diff --git a/README.md b/README.md index c37c8ec..8c4453b 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![license](https://img.shields.io/badge/license-Apache%202.0-red.svg?style=flat)](https://raw.githubusercontent.com/ovh/okms-sdk-go/master/LICENSE) [![Go Report Card](https://goreportcard.com/badge/github.com/ovh/okms-cli)](https://goreportcard.com/report/github.com/ovh/okms-cli) The CLI to interact with your [OVHcloud KMS](https://help.ovhcloud.com/csm/en-ie-kms-quick-start?id=kb_article_view&sysparm_article=KB0063362) services. +It supports both REST API and KMIP protocol. > **NOTE:** THIS PROJECT IS CURRENTLY UNDER DEVELOPMENT AND SUBJECT TO BREAKING CHANGES. @@ -89,6 +90,7 @@ Available Commands: configure Configure CLI options help Help about any command keys Manage domain keys + kmip Manage kmip objects version Print the version information x509 Generate, and sign x509 certificates @@ -116,6 +118,13 @@ profiles: type: mtls # Optional, defaults to "mtls" cert: /path/to/domain/cert.pem key: /path/to/domain/key.pem + kmip: + endpoint: myserver.acme.com:5696 + ca: /path/to/public-ca.crt # Optional if the CA is in system store + auth: + type: mtls # Optional, defaults to "mtls" + cert: /path/to/domain/cert.pem + key: /path/to/domain/key.pem ``` These settings can be overwritten using environment variables: @@ -124,12 +133,24 @@ These settings can be overwritten using environment variables: - KMS_HTTP_CA - KMS_HTTP_CERT - KMS_HTTP_KEY +and +- KMS_KMIP_ENDPOINT +- KMS_KMIP_CA +- KMS_KMIP_CERT +- KMS_KMIP_KEY ```bash -export KMS_HTTP_ENDPOINT=https://the-kms.ovh +# REST API +export KMS_HTTP_ENDPOINT=https://myserver.acme.com export KMS_HTTP_CA=/path/to/certs/ca.crt export KMS_HTTP_CERT=/path/to/certs/user.crt export KMS_HTTP_KEY=/path/to/certs/user.key + +# KMIP +export KMS_KMIP_ENDPOINT=myserver.acme.com:5696 +export KMS_KMIP_CA=/path/to/certs/ca.crt +export KMS_KMIP_CERT=/path/to/certs/user.crt +export KMS_KMIP_KEY=/path/to/certs/user.key ``` but each of them can be overwritten with CLI arguments. diff --git a/cmd/okms/configure/root.go b/cmd/okms/configure/root.go index 2828568..5a1cf0c 100644 --- a/cmd/okms/configure/root.go +++ b/cmd/okms/configure/root.go @@ -42,8 +42,16 @@ func CreateCommand() *cobra.Command { } func Run(profile string) { - config.ReadUserInput("CA file", "http.ca", profile, config.ValidateFileExists.AllowEmpty()) - config.ReadUserInput("Certificate file", "http.auth.cert", profile, config.ValidateFileExists) - config.ReadUserInput("Private key file", "http.auth.key", profile, config.ValidateFileExists) - config.ReadUserInput("Endpoint", "http.endpoint", profile, config.ValidateURL) + choice := exit.OnErr2(pterm.DefaultInteractiveSelect.WithOptions([]string{"HTTP", "KMIP"}).Show("Select a protocol to configure")) + if choice == "HTTP" { + config.ReadUserInput("CA file", "http.ca", profile, config.ValidateFileExists.AllowEmpty()) + config.ReadUserInput("Certificate file", "http.auth.cert", profile, config.ValidateFileExists) + config.ReadUserInput("Private key file", "http.auth.key", profile, config.ValidateFileExists) + config.ReadUserInput("Endpoint", "http.endpoint", profile, config.ValidateURL) + } else if choice == "KMIP" { + config.ReadUserInput("CA file", "kmip.ca", profile, config.ValidateFileExists.AllowEmpty()) + config.ReadUserInput("Certificate file", "kmip.auth.cert", profile, config.ValidateFileExists) + config.ReadUserInput("Private key file", "kmip.auth.key", profile, config.ValidateFileExists) + config.ReadUserInput("Endpoint", "kmip.endpoint", profile, config.ValidateTCPAddr) + } } diff --git a/cmd/okms/kmip/attributes.go b/cmd/okms/kmip/attributes.go new file mode 100644 index 0000000..f070052 --- /dev/null +++ b/cmd/okms/kmip/attributes.go @@ -0,0 +1,77 @@ +package kmip + +import ( + "bytes" + "fmt" + "os" + "regexp" + + "github.com/olekukonko/tablewriter" + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/ttlv" + "github.com/ovh/okms-cli/common/flagsmgmt" + "github.com/ovh/okms-cli/common/output" + "github.com/ovh/okms-cli/common/utils/exit" + "github.com/spf13/cobra" +) + +var ( + attributeValueHdrRegex = regexp.MustCompile(`^AttributeValue \(.+\): `) + attributeValueFieldsRegex = regexp.MustCompile(`(.+) \(.+\): `) +) + +func printAttributeTable(attributes []kmip.Attribute) { + table := tablewriter.NewWriter(os.Stdout) + table.SetAutoWrapText(false) + table.SetHeader([]string{"Name", "Value"}) + table.SetRowLine(true) + table.SetAlignment(tablewriter.ALIGN_LEFT) + + enc := ttlv.NewTextEncoder() + for _, attr := range attributes { + enc.Clear() + enc.TagAny(kmip.TagAttributeValue, attr.AttributeValue) + txt := enc.Bytes() + + txt = attributeValueHdrRegex.ReplaceAll(txt, nil) + txt = attributeValueFieldsRegex.ReplaceAll(txt, []byte("$1: ")) + txt = bytes.ReplaceAll(txt, []byte("\n "), []byte("\n")) + txt = bytes.TrimSpace(txt) + + name := string(attr.AttributeName) + if idx := attr.AttributeIndex; idx != nil && *idx > 0 { + name = fmt.Sprintf("%s [%d]", name, *idx) + } + table.Append([]string{name, string(txt)}) + } + + table.Render() +} + +func getAttributesCommand() *cobra.Command { + return &cobra.Command{ + Use: "get ID", + Short: "Get the attributes of an object", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + attributes := exit.OnErr2(kmipClient.GetAttributes(args[0]).ExecContext(cmd.Context())) + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(attributes) + return + } + printAttributeTable(attributes.Attribute) + }, + } +} + +func attributesCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "attributes", + Aliases: []string{"attribute", "attr"}, + Short: "Manage an object's attributes", + } + cmd.AddCommand( + getAttributesCommand(), + ) + return cmd +} diff --git a/cmd/okms/kmip/create.go b/cmd/okms/kmip/create.go new file mode 100644 index 0000000..4d93244 --- /dev/null +++ b/cmd/okms/kmip/create.go @@ -0,0 +1,159 @@ +package kmip + +import ( + "errors" + "fmt" + + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/kmipclient" + "github.com/ovh/okms-cli/common/flagsmgmt" + "github.com/ovh/okms-cli/common/flagsmgmt/kmipflags" + "github.com/ovh/okms-cli/common/output" + "github.com/ovh/okms-cli/common/utils/exit" + "github.com/spf13/cobra" +) + +func createCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "create", + Short: "Create kmip keys", + } + cmd.AddCommand( + createSymmetricKey(), + createKeyPair(), + ) + return cmd +} + +func createSymmetricKey() *cobra.Command { + cmd := &cobra.Command{ + Use: "symmetric", + Aliases: []string{"sym"}, + Short: "Create KMIP symmetric key", + Args: cobra.NoArgs, + } + + var alg kmipflags.SymmetricAlg + usage := kmipflags.KeyUsageList{kmipflags.ENCRYPT, kmipflags.DECRYPT} + + cmd.Flags().Var(&alg, "alg", "Key algorithm") + size := cmd.Flags().Int("size", 0, "Key bit length") + cmd.Flags().Var(&usage, "usage", "Cryptographic usage") + name := cmd.Flags().String("name", "", "Optional key name") + + sensitive := cmd.Flags().Bool("sensitive", false, "Set sensitive attribute") + extractable := cmd.Flags().Bool("extractable", true, "Set the extractable attribute") + description := cmd.Flags().String("description", "", "Set the description attribute") + comment := cmd.Flags().String("comment", "", "Set the comment attribute") + + _ = cmd.MarkFlagRequired("alg") + _ = cmd.MarkFlagRequired("size") + + cmd.Run = func(cmd *cobra.Command, args []string) { + req := kmipClient.Create(). + SymmetricKey(kmip.CryptographicAlgorithm(alg), *size, usage.ToCryptographicUsageMask()). + WithAttribute(kmip.AttributeNameExtractable, *extractable). + WithAttribute(kmip.AttributeNameSensitive, *sensitive) + if *name != "" { + req = req.WithName(*name) + } + if *description != "" { + req = req.WithAttribute(kmip.AttributeNameDescription, *description) + } + if *comment != "" { + req = req.WithAttribute(kmip.AttributeNameComment, *comment) + } + + resp := exit.OnErr2(req.ExecContext(cmd.Context())) + + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + } else { + fmt.Println("Key created with ID", resp.UniqueIdentifier) + // Print returned attributes if any + if resp.Attributes != nil && len(resp.Attributes.Attribute) > 0 { + printAttributeTable(resp.Attributes.Attribute) + } + } + } + + return cmd +} + +func createKeyPair() *cobra.Command { + cmd := &cobra.Command{ + Use: "key-pair", + Short: "Create an asymmetric key-pair", + Args: cobra.NoArgs, + } + + var alg kmipflags.AsymmetricAlg + cmd.Flags().Var(&alg, "alg", "Key-pair algorithm") + size := cmd.Flags().Int("size", 0, "Modulus bit length of the RSA key-pair to generate") + var curve kmipflags.EcCurve + cmd.Flags().Var(&curve, "curve", "Elliptic curve for EC keys") + privateUsage := kmipflags.KeyUsageList{kmipflags.SIGN} + publicUsage := kmipflags.KeyUsageList{kmipflags.VERIFY} + cmd.Flags().Var(&privateUsage, "private-usage", "Private key allowed usage") + cmd.Flags().Var(&publicUsage, "public-usage", "Public key allowed usage") + privateName := cmd.Flags().String("private-name", "", "Optional private key name") + publicName := cmd.Flags().String("public-name", "", "Optional public key name") + + privateSensitive := cmd.Flags().Bool("private-sensitive", false, "Set sensitive attribute on the private key") + privateExtractable := cmd.Flags().Bool("private-extractable", true, "Set the extractable attribute on the private key") + description := cmd.Flags().String("description", "", "Set the description attribute on both keys") + comment := cmd.Flags().String("comment", "", "Set the comment attribute on both keys") + + _ = cmd.MarkFlagRequired("alg") + cmd.MarkFlagsMutuallyExclusive("curve", "size") + + cmd.Run = func(cmd *cobra.Command, args []string) { + var req kmipclient.ExecCreateKeyPairAttr + switch alg { + case kmipflags.RSA: + if *size == 0 { + exit.OnErr(errors.New("Missing --size flag")) + } + req = kmipClient.CreateKeyPair().RSA(*size, privateUsage.ToCryptographicUsageMask(), publicUsage.ToCryptographicUsageMask()) + case kmipflags.ECDSA: + if curve == 0 { + exit.OnErr(errors.New("Missing --curve flag")) + } + req = kmipClient.CreateKeyPair().ECDSA(kmip.RecommendedCurve(curve), privateUsage.ToCryptographicUsageMask(), publicUsage.ToCryptographicUsageMask()) + } + if *privateName != "" { + req = req.PrivateKey().WithName(*privateName) + } + if *publicName != "" { + req = req.PublicKey().WithName(*publicName) + } + req = req.PrivateKey(). + WithAttribute(kmip.AttributeNameExtractable, *privateExtractable). + WithAttribute(kmip.AttributeNameSensitive, *privateSensitive) + if *description != "" { + req = req.Common().WithAttribute(kmip.AttributeNameDescription, *description) + } + if *comment != "" { + req = req.Common().WithAttribute(kmip.AttributeNameComment, *comment) + } + resp := exit.OnErr2(req.ExecContext(cmd.Context())) + + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + } else { + fmt.Println("Pubic Key ID:", resp.PublicKeyUniqueIdentifier) + fmt.Println("Private Key ID:", resp.PrivateKeyUniqueIdentifier) + // Print returned attributes if any + if attrs := resp.PublicKeyTemplateAttribute; attrs != nil && len(attrs.Attribute) > 0 { + fmt.Println("Public Key Attributes:") + printAttributeTable(attrs.Attribute) + } + if attrs := resp.PrivateKeyTemplateAttribute; attrs != nil && len(attrs.Attribute) > 0 { + fmt.Println("Private Key Attributes:") + printAttributeTable(attrs.Attribute) + } + } + } + + return cmd +} diff --git a/cmd/okms/kmip/get.go b/cmd/okms/kmip/get.go new file mode 100644 index 0000000..15005ad --- /dev/null +++ b/cmd/okms/kmip/get.go @@ -0,0 +1,53 @@ +package kmip + +import ( + "fmt" + "os" + + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/ttlv" + "github.com/ovh/okms-cli/common/flagsmgmt" + "github.com/ovh/okms-cli/common/output" + "github.com/ovh/okms-cli/common/utils/exit" + "github.com/spf13/cobra" +) + +func getCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "get ID", + Short: "Get the materials from a kmip object", + Args: cobra.ExactArgs(1), + } + + cmd.Run = func(cmd *cobra.Command, args []string) { + req := kmipClient.Get(args[0]) + + resp := exit.OnErr2(req.ExecContext(cmd.Context())) + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + return + } + + switch obj := resp.Object.(type) { + case *kmip.SecretData: + secret := exit.OnErr2(obj.Data()) + os.Stdout.Write(secret) + case *kmip.SymmetricKey: + key := exit.OnErr2(obj.KeyMaterial()) + os.Stdout.Write(key) + case *kmip.Certificate: + cert := exit.OnErr2(obj.PemCertificate()) + fmt.Println(cert) + case *kmip.PrivateKey: + pem := exit.OnErr2(obj.Pkcs8Pem()) + fmt.Println(pem) + case *kmip.PublicKey: + pem := exit.OnErr2(obj.PkixPem()) + fmt.Println(pem) + default: + os.Stdout.Write(ttlv.MarshalText(resp)) + } + } + + return cmd +} diff --git a/cmd/okms/kmip/locate.go b/cmd/okms/kmip/locate.go new file mode 100644 index 0000000..0ca52c5 --- /dev/null +++ b/cmd/okms/kmip/locate.go @@ -0,0 +1,95 @@ +package kmip + +import ( + "os" + "strconv" + + "github.com/olekukonko/tablewriter" + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/payloads" + "github.com/ovh/kmip-go/ttlv" + "github.com/ovh/okms-cli/common/flagsmgmt" + "github.com/ovh/okms-cli/common/flagsmgmt/kmipflags" + "github.com/ovh/okms-cli/common/output" + "github.com/ovh/okms-cli/common/utils/exit" + "github.com/spf13/cobra" +) + +func locateCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "locate", + Aliases: []string{"list", "ls"}, + Short: "List kmip objects", + Args: cobra.NoArgs, + } + + detailed := cmd.Flags().Bool("details", false, "Display detailed information") + var state kmipflags.State + cmd.Flags().Var(&state, "state", "List only object with the given state") + var objectType kmipflags.ObjectType + cmd.Flags().Var(&objectType, "type", "List only objects of the given type") + + cmd.Run = func(cmd *cobra.Command, args []string) { + req := kmipClient.Locate() + if state != 0 { + req = req.WithAttribute(kmip.AttributeNameState, kmip.State(state)) + } + if objectType != 0 { + req = req.WithObjectType(kmip.ObjectType(objectType)) + } + locateResp := exit.OnErr2(req.ExecContext(cmd.Context())) + if !*detailed { + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(locateResp) + } else { + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Id"}) + for _, id := range locateResp.UniqueIdentifier { + table.Append([]string{id}) + } + table.Render() + } + return + } + + attributes := []*payloads.GetAttributesResponsePayload{} + for _, id := range locateResp.UniqueIdentifier { + attributes = append(attributes, exit.OnErr2(kmipClient.GetAttributes(id).ExecContext(cmd.Context()))) + } + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(attributes) + } else { + printObjectTable(attributes) + } + } + + return cmd +} + +func printObjectTable(objects []*payloads.GetAttributesResponsePayload) { + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"ID", "TYPE", "NAME", "STATE", "ALGORITHM", "SIZE"}) + for _, attr := range objects { + var row [6]string + row[0] = attr.UniqueIdentifier + for _, v := range attr.Attribute { + if idx := v.AttributeIndex; idx != nil && *idx > 0 { + continue + } + switch v.AttributeName { + case kmip.AttributeNameObjectType: + row[1] = ttlv.EnumStr(v.AttributeValue.(kmip.ObjectType)) + case kmip.AttributeNameName: + row[2] = v.AttributeValue.(kmip.Name).NameValue + case kmip.AttributeNameState: + row[3] = ttlv.EnumStr(v.AttributeValue.(kmip.State)) + case kmip.AttributeNameCryptographicAlgorithm: + row[4] = ttlv.EnumStr(v.AttributeValue.(kmip.CryptographicAlgorithm)) + case kmip.AttributeNameCryptographicLength: + row[5] = strconv.Itoa(int(v.AttributeValue.(int32))) + } + } + table.Append(row[:]) + } + table.Render() +} diff --git a/cmd/okms/kmip/register.go b/cmd/okms/kmip/register.go new file mode 100644 index 0000000..8e80f47 --- /dev/null +++ b/cmd/okms/kmip/register.go @@ -0,0 +1,387 @@ +package kmip + +import ( + "encoding/base64" + "fmt" + + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/kmipclient" + "github.com/ovh/kmip-go/payloads" + "github.com/ovh/okms-cli/common/flagsmgmt" + "github.com/ovh/okms-cli/common/flagsmgmt/kmipflags" + "github.com/ovh/okms-cli/common/output" + "github.com/ovh/okms-cli/common/utils/exit" + "github.com/spf13/cobra" +) + +func registerCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "register", + Short: "Register a kmip object", + } + + cmd.AddCommand( + registerSecretCommand(), + registerSymmetricKeyCommand(), + registerCertificateCommand(), + registerPublicKeyCommand(), + registerPrivateKeyCommand(), + registerKeyPairCommand(), + ) + + return cmd +} + +func registerSecretCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "secret VALUE", + Short: "Register a secret object", + Long: `Register a secret object. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @.`, + Args: cobra.ExactArgs(1), + } + + b64 := cmd.Flags().Bool("base64", false, "Given secret is base64 encoded") + name := cmd.Flags().String("name", "", "Optional name for the secret") + description := cmd.Flags().String("description", "", "Set the description attribute") + comment := cmd.Flags().String("comment", "", "Set the comment attribute") + + cmd.Run = func(cmd *cobra.Command, args []string) { + secret := flagsmgmt.BytesFromArg(args[0], 16_000) + if *b64 { + secret = exit.OnErr2(base64.StdEncoding.AppendDecode(nil, secret)) + } + //TODO: Make secret type a flag argument + req := kmipClient.Register().Secret(kmip.Password, secret) + if *name != "" { + req = req.WithName(*name) + } + if *description != "" { + req = req.WithAttribute(kmip.AttributeNameDescription, *description) + } + if *comment != "" { + req = req.WithAttribute(kmip.AttributeNameComment, *comment) + } + resp := exit.OnErr2(req.ExecContext(cmd.Context())) + + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + } else { + fmt.Println("Secret registered with ID", resp.UniqueIdentifier) + // Print returned attributes if any + if attr := resp.TemplateAttribute; attr != nil && len(attr.Attribute) > 0 { + printAttributeTable(attr.Attribute) + } + } + } + + return cmd +} + +func registerSymmetricKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "symmetric VALUE", + Aliases: []string{"sym"}, + Short: "Register a symmetric key object", + Long: `Register a symmetric key object. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @.`, + Args: cobra.ExactArgs(1), + } + + var alg kmipflags.SymmetricAlg + cmd.Flags().Var(&alg, "alg", "Key's cryptographic algorithm") + + usage := kmipflags.KeyUsageList{kmipflags.ENCRYPT, kmipflags.DECRYPT} + cmd.Flags().Var(&usage, "usage", "Cryptographic usage") + + b64 := cmd.Flags().Bool("base64", false, "Given key is base64 encoded") + name := cmd.Flags().String("name", "", "Optional name for the key") + description := cmd.Flags().String("description", "", "Set the description attribute") + comment := cmd.Flags().String("comment", "", "Set the comment attribute") + sensitive := cmd.Flags().Bool("sensitive", false, "Set sensitive attribute") + extractable := cmd.Flags().Bool("extractable", true, "Set the extractable attribute") + + _ = cmd.MarkFlagRequired("alg") + + cmd.Run = func(cmd *cobra.Command, args []string) { + key := flagsmgmt.BytesFromArg(args[0], 16_000) + if *b64 { + key = exit.OnErr2(base64.StdEncoding.AppendDecode(nil, key)) + } + + req := kmipClient.Register(). + SymmetricKey(kmip.CryptographicAlgorithm(alg), usage.ToCryptographicUsageMask(), key). + WithAttribute(kmip.AttributeNameExtractable, *extractable). + WithAttribute(kmip.AttributeNameSensitive, *sensitive) + if *name != "" { + req = req.WithName(*name) + } + if *description != "" { + req = req.WithAttribute(kmip.AttributeNameDescription, *description) + } + if *comment != "" { + req = req.WithAttribute(kmip.AttributeNameComment, *comment) + } + resp := exit.OnErr2(req.ExecContext(cmd.Context())) + + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + } else { + fmt.Println("Symmetric key registered with ID", resp.UniqueIdentifier) + // Print returned attributes if any + if attr := resp.TemplateAttribute; attr != nil && len(attr.Attribute) > 0 { + printAttributeTable(attr.Attribute) + } + } + } + + return cmd +} + +func registerCertificateCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "certificate VALUE", + Aliases: []string{"cert", "crt"}, + Short: "Register an X509 certificate", + Long: `Register an X509 certificate. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @.`, + Args: cobra.ExactArgs(1), + } + + isPem := cmd.Flags().Bool("pem", false, "Certificate is PEM encoded") + name := cmd.Flags().String("name", "", "Optional name for the certificate") + description := cmd.Flags().String("description", "", "Set the description attribute") + comment := cmd.Flags().String("comment", "", "Set the comment attribute") + + cmd.Run = func(cmd *cobra.Command, args []string) { + cert := flagsmgmt.BytesFromArg(args[0], 16_000) + + var req kmipclient.ExecRegister + if *isPem { + req = kmipClient.Register().PemCertificate(cert) + } else { + req = kmipClient.Register().Certificate(kmip.X_509, cert) + } + + if *name != "" { + req = req.WithName(*name) + } + if *description != "" { + req = req.WithAttribute(kmip.AttributeNameDescription, *description) + } + if *comment != "" { + req = req.WithAttribute(kmip.AttributeNameComment, *comment) + } + resp := exit.OnErr2(req.ExecContext(cmd.Context())) + + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + } else { + fmt.Println("Certificate registered with ID", resp.UniqueIdentifier) + // Print returned attributes if any + if attr := resp.TemplateAttribute; attr != nil && len(attr.Attribute) > 0 { + printAttributeTable(attr.Attribute) + } + } + } + + return cmd +} + +func registerPublicKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "public-key VALUE", + Aliases: []string{"public", "pub"}, + Short: "Register a public key object from PEM encoded data", + Long: `Register a public key object from PEM encoded data. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @.`, + Args: cobra.ExactArgs(1), + } + usage := kmipflags.KeyUsageList{kmipflags.VERIFY} + cmd.Flags().Var(&usage, "usage", "Cryptographic usage") + + name := cmd.Flags().String("name", "", "Optional name for the key") + description := cmd.Flags().String("description", "", "Set the description attribute") + comment := cmd.Flags().String("comment", "", "Set the comment attribute") + + privLink := cmd.Flags().String("private-link", "", "Optional private key ID to link to") + + cmd.Run = func(cmd *cobra.Command, args []string) { + key := flagsmgmt.BytesFromArg(args[0], 16_000) + + req := kmipClient.Register().PemPublicKey(key, usage.ToCryptographicUsageMask()) + if *name != "" { + req = req.WithName(*name) + } + if *description != "" { + req = req.WithAttribute(kmip.AttributeNameDescription, *description) + } + if *comment != "" { + req = req.WithAttribute(kmip.AttributeNameComment, *comment) + } + if *privLink != "" { + req = req.WithLink(kmip.PrivateKeyLink, *privLink) + } + resp := exit.OnErr2(req.ExecContext(cmd.Context())) + + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + } else { + fmt.Println("Public key registered with ID", resp.UniqueIdentifier) + // Print returned attributes if any + if attr := resp.TemplateAttribute; attr != nil && len(attr.Attribute) > 0 { + printAttributeTable(attr.Attribute) + } + } + } + + return cmd +} + +func registerPrivateKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "private-key VALUE", + Aliases: []string{"private", "priv"}, + Short: "Register a private key object from PEM encoded data", + Long: `Register a private key object from PEM encoded data. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @.`, + Args: cobra.ExactArgs(1), + } + usage := kmipflags.KeyUsageList{kmipflags.SIGN} + cmd.Flags().Var(&usage, "usage", "Cryptographic usage") + + name := cmd.Flags().String("name", "", "Optional name for the key") + description := cmd.Flags().String("description", "", "Set the description attribute") + comment := cmd.Flags().String("comment", "", "Set the comment attribute") + + sensitive := cmd.Flags().Bool("sensitive", false, "Set sensitive attribute") + extractable := cmd.Flags().Bool("extractable", true, "Set the extractable attribute") + + pubLink := cmd.Flags().String("public-link", "", "Optional public key ID to link to") + + cmd.Run = func(cmd *cobra.Command, args []string) { + key := flagsmgmt.BytesFromArg(args[0], 16_000) + + req := kmipClient.Register().PemPrivateKey(key, usage.ToCryptographicUsageMask()). + WithAttribute(kmip.AttributeNameExtractable, *extractable). + WithAttribute(kmip.AttributeNameSensitive, *sensitive) + if *name != "" { + req = req.WithName(*name) + } + if *description != "" { + req = req.WithAttribute(kmip.AttributeNameDescription, *description) + } + if *comment != "" { + req = req.WithAttribute(kmip.AttributeNameComment, *comment) + } + if *pubLink != "" { + req = req.WithLink(kmip.PublicKeyLink, *pubLink) + } + resp := exit.OnErr2(req.ExecContext(cmd.Context())) + + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + } else { + fmt.Println("Private key registered with ID", resp.UniqueIdentifier) + // Print returned attributes if any + if attr := resp.TemplateAttribute; attr != nil && len(attr.Attribute) > 0 { + printAttributeTable(attr.Attribute) + } + } + } + + return cmd +} + +func registerKeyPairCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "key-pair VALUE", + Aliases: []string{"pair", "kp"}, + Short: "Register a private and a public key objects from private key PEM encoded data", + Long: `Register a private and a public key objects from private key PEM encoded data. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @.`, + Args: cobra.ExactArgs(1), + } + + privateUsage := kmipflags.KeyUsageList{kmipflags.SIGN} + publicUsage := kmipflags.KeyUsageList{kmipflags.VERIFY} + cmd.Flags().Var(&privateUsage, "private-usage", "Private key allowed usage") + cmd.Flags().Var(&publicUsage, "public-usage", "Public key allowed usage") + privateName := cmd.Flags().String("private-name", "", "Optional private key name") + publicName := cmd.Flags().String("public-name", "", "Optional public key name") + + privateSensitive := cmd.Flags().Bool("private-sensitive", false, "Set sensitive attribute on the private key") + privateExtractable := cmd.Flags().Bool("private-extractable", true, "Set the extractable attribute on the private key") + description := cmd.Flags().String("description", "", "Set the description attribute on both keys") + comment := cmd.Flags().String("comment", "", "Set the comment attribute on both keys") + + cmd.Run = func(cmd *cobra.Command, args []string) { + key := flagsmgmt.BytesFromArg(args[0], 16_000) + + // Register private key + privReq := kmipClient.Register().PemPrivateKey(key, privateUsage.ToCryptographicUsageMask()). + WithAttribute(kmip.AttributeNameExtractable, *privateExtractable). + WithAttribute(kmip.AttributeNameSensitive, *privateSensitive) + if *privateName != "" { + privReq = privReq.WithName(*privateName) + } + if *description != "" { + privReq = privReq.WithAttribute(kmip.AttributeNameDescription, *description) + } + if *comment != "" { + privReq = privReq.WithAttribute(kmip.AttributeNameComment, *comment) + } + privResp := exit.OnErr2(privReq.ExecContext(cmd.Context())) + + // Register public key + pubReq := kmipClient.Register().PemPublicKey(key, publicUsage.ToCryptographicUsageMask()). + WithLink(kmip.PrivateKeyLink, privResp.UniqueIdentifier) + if *publicName != "" { + pubReq = pubReq.WithName(*publicName) + } + if *description != "" { + pubReq = pubReq.WithAttribute(kmip.AttributeNameDescription, *description) + } + if *comment != "" { + pubReq = pubReq.WithAttribute(kmip.AttributeNameComment, *comment) + } + pubResp := exit.OnErr2(pubReq.ExecContext(cmd.Context())) + + // Update public key link in private key + exit.OnErr2(kmipClient.AddAttribute(privResp.UniqueIdentifier, kmip.AttributeNameLink, kmip.Link{ + LinkType: kmip.PublicKeyLink, + LinkedObjectIdentifier: pubResp.UniqueIdentifier, + }).ExecContext(cmd.Context())) + + resp := &payloads.CreateKeyPairResponsePayload{ + PrivateKeyUniqueIdentifier: privResp.UniqueIdentifier, + PublicKeyUniqueIdentifier: pubResp.UniqueIdentifier, + PrivateKeyTemplateAttribute: privResp.TemplateAttribute, + PublicKeyTemplateAttribute: pubResp.TemplateAttribute, + } + + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + } else { + fmt.Println("Pubic Key registered with ID:", resp.PublicKeyUniqueIdentifier) + fmt.Println("Private Key registered with ID:", resp.PrivateKeyUniqueIdentifier) + // Print returned attributes if any + if attrs := resp.PublicKeyTemplateAttribute; attrs != nil && len(attrs.Attribute) > 0 { + fmt.Println("Public Key Attributes:") + printAttributeTable(attrs.Attribute) + } + if attrs := resp.PrivateKeyTemplateAttribute; attrs != nil && len(attrs.Attribute) > 0 { + fmt.Println("Private Key Attributes:") + printAttributeTable(attrs.Attribute) + } + } + } + + return cmd +} diff --git a/cmd/okms/kmip/rekey.go b/cmd/okms/kmip/rekey.go new file mode 100644 index 0000000..f9274e0 --- /dev/null +++ b/cmd/okms/kmip/rekey.go @@ -0,0 +1,64 @@ +package kmip + +import ( + "errors" + "fmt" + + "github.com/ovh/okms-cli/common/flagsmgmt" + "github.com/ovh/okms-cli/common/output" + "github.com/ovh/okms-cli/common/utils/exit" + "github.com/spf13/cobra" +) + +func rekeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "rekey ID", + Short: "Rekey a symmetric key object", + Args: cobra.ExactArgs(1), + } + + offset := cmd.Flags().Duration("offset", 0, "Optional rekeying offset") + + // cmd.Flags().Var(&usage, "usage", "Cryptographic usage") + // name := cmd.Flags().String("name", "", "Optional key name") + + // sensitive := cmd.Flags().Bool("sensitive", false, "Change sensitive attribute") + // extractable := cmd.Flags().Bool("extractable", false, "Change the extractable attribute") + // description := cmd.Flags().String("description", "", "Change the description attribute") + // comment := cmd.Flags().String("comment", "", "Change the comment attribute") + + cmd.Run = func(cmd *cobra.Command, args []string) { + req := kmipClient.Rekey(args[0]) + + if cmd.Flag("offset").Changed { + if *offset < 0 { + exit.OnErr(errors.New("offset cannot be negative")) + } + req = req.WithOffset(*offset) + } + // if cmd.Flag("sensitive").Changed { + // req = req.WithAttribute(kmip.AttributeNameSensitive, *sensitive) + // } + // if cmd.Flag("extractable").Changed { + // req = req.WithAttribute(kmip.AttributeNameExtractable, *extractable) + // } + // if *description != "" { + // req = req.WithAttribute(kmip.AttributeNameDescription, *description) + // } + // if *comment != "" { + // req = req.WithAttribute(kmip.AttributeNameComment, *comment) + // } + + resp := exit.OnErr2(req.ExecContext(cmd.Context())) + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + } else { + fmt.Println("Replacement key ID:", resp.UniqueIdentifier) + if attr := resp.TemplateAttribute; attr != nil && len(attr.Attribute) > 0 { + printAttributeTable(attr.Attribute) + } + } + } + + return cmd +} diff --git a/cmd/okms/kmip/root.go b/cmd/okms/kmip/root.go new file mode 100644 index 0000000..e56e16a --- /dev/null +++ b/cmd/okms/kmip/root.go @@ -0,0 +1,151 @@ +package kmip + +import ( + "errors" + "fmt" + "os" + + "github.com/google/uuid" + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/kmipclient" + "github.com/ovh/kmip-go/ttlv" + "github.com/ovh/okms-cli/common/config" + "github.com/ovh/okms-cli/common/flagsmgmt" + "github.com/ovh/okms-cli/common/flagsmgmt/kmipflags" + "github.com/ovh/okms-cli/common/output" + "github.com/ovh/okms-cli/common/utils/exit" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +type CustomizeFunc func(c *cobra.Command) func(*[]kmipclient.Option) + +var kmipClient *kmipclient.Client + +func SetupKmipFlags(command *cobra.Command, cust CustomizeFunc) { + debug := command.PersistentFlags().BoolP("debug", "d", false, "Activate debug mode") + // retry := command.PersistentFlags().Uint32("retry", 4, "Maximum number of HTTP retries") + // timeout := command.PersistentFlags().Duration("timeout", okms.DefaultHTTPClientTimeout, "Timeout duration for HTTP requests") + + f := func(*[]kmipclient.Option) {} + if cust != nil { + f = cust(command) + } + + config.SetupEndpointFlags(command, "kmip", func(command *cobra.Command, cfg config.EndpointConfig) { + middlewares := []kmipclient.Middleware{ + kmipclient.CorrelationValueMiddleware(uuid.NewString), + } + if *debug { + middlewares = append(middlewares, kmipclient.DebugMiddleware(os.Stderr, ttlv.MarshalXML)) + } + opts := []kmipclient.Option{ + kmipclient.WithTlsConfig(cfg.TlsConfig("")), + kmipclient.WithMiddlewares(middlewares...), + } + f(&opts) + kmipClient = exit.OnErr2(kmipclient.Dial( + cfg.Endpoint, + opts..., + )) + }) +} + +func NewCommand(cust CustomizeFunc) *cobra.Command { + cmd := &cobra.Command{ + Use: "kmip", + Short: "Manage kmip objects", + } + SetupKmipFlags(cmd, cust) + + cmd.AddCommand( + locateCommand(), + createCommand(), + attributesCommand(), + activateCommand(), + revokeCommand(), + destroyCommand(), + getCommand(), + registerCommand(), + rekeyCommand(), + ) + + return cmd +} + +func activateCommand() *cobra.Command { + return &cobra.Command{ + Use: "activate ID", + Short: "Activate an object", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + resp := exit.OnErr2(kmipClient.Activate(args[0]).ExecContext(cmd.Context())) + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + return + } + fmt.Println("Activated object", resp.UniqueIdentifier) + }, + } +} + +func revokeCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "revoke ID", + Short: "Revoke an object", + Args: cobra.ExactArgs(1), + } + + reason := kmipflags.Unspecified + cmd.Flags().Var(&reason, "reason", "Revocation reason") + msg := cmd.Flags().String("message", "", "Optional revocation message") + force := cmd.Flags().Bool("force", false, "Force revoke without prompting for confirmation") + + cmd.Run = func(cmd *cobra.Command, args []string) { + if !*force { + if ok, _ := pterm.DefaultInteractiveConfirm.Show("Revocation cannot be undone. Continue ?"); !ok { + exit.OnErr(errors.New("Canceled")) + } + } + req := kmipClient.Revoke(args[0]).WithRevocationReasonCode(kmip.RevocationReasonCode(reason)) + if *msg != "" { + req = req.WithRevocationMessage(*msg) + } + + resp := exit.OnErr2(req.ExecContext(cmd.Context())) + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + return + } + fmt.Println("Revoked object", resp.UniqueIdentifier) + } + + return cmd +} + +func destroyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "destroy ID", + Aliases: []string{"delete", "del"}, + Short: "Destroy an object", + Args: cobra.ExactArgs(1), + } + + force := cmd.Flags().Bool("force", false, "Force deleton without prompting for confirmation") + + cmd.Run = func(cmd *cobra.Command, args []string) { + if !*force { + if ok, _ := pterm.DefaultInteractiveConfirm.Show("Destroy cannot be undone. Continue ?"); !ok { + exit.OnErr(errors.New("Canceled")) + } + } + resp := exit.OnErr2(kmipClient.Destroy(args[0]).ExecContext(cmd.Context())) + if cmd.Flag("output").Value.String() == string(flagsmgmt.JSON_OUTPUT_FORMAT) { + output.JsonPrint(resp) + return + } + fmt.Println("Destroyed object", resp.UniqueIdentifier) + } + + return cmd +} diff --git a/cmd/okms/main.go b/cmd/okms/main.go index 2386342..9753c00 100644 --- a/cmd/okms/main.go +++ b/cmd/okms/main.go @@ -7,6 +7,7 @@ import ( "github.com/ovh/okms-cli/cmd/okms/configure" "github.com/ovh/okms-cli/cmd/okms/keys" + "github.com/ovh/okms-cli/cmd/okms/kmip" "github.com/ovh/okms-cli/cmd/okms/x509" "github.com/ovh/okms-cli/common/commands" @@ -37,6 +38,7 @@ func createRootCommand() *cobra.Command { keys.CreateCommand(nil), // secrets.CreateCommand(nil), x509.CreateX509Command(nil), + kmip.NewCommand(nil), configure.CreateCommand(), commands.NewMarkdownCmd(command), commands.NewVersionCmd(&version, &commit, &date), diff --git a/cmd/okms/okms.sample.yaml b/cmd/okms/okms.sample.yaml index 22895ef..74585db 100644 --- a/cmd/okms/okms.sample.yaml +++ b/cmd/okms/okms.sample.yaml @@ -9,6 +9,13 @@ profiles: type: mtls # Optional, defaults to "mtls" cert: /path/to/domain/cert.pem key: /path/to/domain/key.pem + kmip: + endpoint: myserver.acme.com:5696 + ca: /path/to/public-ca.crt # Optional if the CA is in system store + auth: + type: mtls # Optional, defaults to "mtls" + cert: /path/to/domain/cert.pem + key: /path/to/domain/key.pem my-profile: http: endpoint: https://my-profile.kms.ovh:8080 @@ -16,4 +23,4 @@ profiles: auth: type: yubikey # Requires yubikey built-in support slot: 9a # Optional, defaults to authentication slot (0x9A). See https://developers.yubico.com/PIV/Introduction/Certificate_slots.html - cert: /path/to/domain/cert.pem # Optional if the certificate is registered in the yubikey \ No newline at end of file + cert: /path/to/domain/cert.pem # Optional if the certificate is registered in the yubikey diff --git a/common/flagsmgmt/kmipflags/algorithms.go b/common/flagsmgmt/kmipflags/algorithms.go new file mode 100644 index 0000000..83113f7 --- /dev/null +++ b/common/flagsmgmt/kmipflags/algorithms.go @@ -0,0 +1,69 @@ +package kmipflags + +import ( + "errors" + "strings" + + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/ttlv" +) + +type SymmetricAlg kmip.CryptographicAlgorithm + +const ( + AES SymmetricAlg = SymmetricAlg(kmip.AES) + TDES SymmetricAlg = SymmetricAlg(kmip.TDES) + SKIPJACK SymmetricAlg = SymmetricAlg(kmip.SKIPJACK) +) + +func (e *SymmetricAlg) String() string { + return ttlv.EnumStr(kmip.CryptographicAlgorithm(*e)) +} + +func (e *SymmetricAlg) Set(v string) error { + switch strings.ToLower(v) { + case "aes": + *e = AES + case "tdes": + *e = TDES + case "skipjack": + *e = SKIPJACK + default: + return errors.New(`must be one of "AES", "TDES", "SKIPJACK"`) + } + return nil +} + +func (e *SymmetricAlg) Type() string { + return "AES|TDES|SKIPJACK" +} + +type AsymmetricAlg kmip.CryptographicAlgorithm + +const ( + RSA AsymmetricAlg = AsymmetricAlg(kmip.RSA) + ECDSA AsymmetricAlg = AsymmetricAlg(kmip.ECDSA) +) + +func (e *AsymmetricAlg) String() string { + if *e == 0 { + return "" + } + return ttlv.EnumStr(kmip.CryptographicAlgorithm(*e)) +} + +func (e *AsymmetricAlg) Set(v string) error { + switch strings.ToLower(v) { + case "rsa": + *e = RSA + case "ecdsa": + *e = ECDSA + default: + return errors.New(`must be one of "RSA", "ECDSA"`) + } + return nil +} + +func (e *AsymmetricAlg) Type() string { + return "RSA|ECDSA" +} diff --git a/common/flagsmgmt/kmipflags/curve.go b/common/flagsmgmt/kmipflags/curve.go new file mode 100644 index 0000000..1b983ee --- /dev/null +++ b/common/flagsmgmt/kmipflags/curve.go @@ -0,0 +1,48 @@ +package kmipflags + +import ( + "errors" + "strings" + + "github.com/ovh/kmip-go" +) + +type EcCurve kmip.RecommendedCurve + +const ( + P256 EcCurve = EcCurve(kmip.P_256) + P384 EcCurve = EcCurve(kmip.P_384) + P521 EcCurve = EcCurve(kmip.P_521) +) + +func (e *EcCurve) String() string { + switch *e { + case 0: + return "" + case P256: + return "P-256" + case P384: + return "P-384" + case P521: + return "P-521" + } + panic("unreachable") +} + +func (e *EcCurve) Set(v string) error { + switch strings.ToLower(v) { + case "p-256": + *e = P256 + case "p-384": + *e = P384 + case "p-521": + *e = P521 + default: + return errors.New(`must be one of "P-256", "P-384", "P-521"`) + } + return nil +} + +func (e *EcCurve) Type() string { + return "P-256|P-384|P-521" +} diff --git a/common/flagsmgmt/kmipflags/key_usage.go b/common/flagsmgmt/kmipflags/key_usage.go new file mode 100644 index 0000000..676a01b --- /dev/null +++ b/common/flagsmgmt/kmipflags/key_usage.go @@ -0,0 +1,103 @@ +package kmipflags + +import ( + "errors" + "strings" + + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/ttlv" +) + +type KeyUsage kmip.CryptographicUsageMask + +const ( + SIGN KeyUsage = KeyUsage(kmip.Sign) + VERIFY KeyUsage = KeyUsage(kmip.Verify) + ENCRYPT KeyUsage = KeyUsage(kmip.Encrypt) + DECRYPT KeyUsage = KeyUsage(kmip.Decrypt) + WRAPKEY KeyUsage = KeyUsage(kmip.WrapKey) + UNWRAPKEY KeyUsage = KeyUsage(kmip.UnwrapKey) + DERIVEKEY KeyUsage = KeyUsage(kmip.DeriveKey) +) + +func (e *KeyUsage) String() string { + return ttlv.BitmaskStr(kmip.CryptographicUsageMask(*e), ",") + // return "TODO" +} + +func (e *KeyUsage) Set(v string) error { + switch strings.ToLower(v) { + case "sign": + *e = SIGN + case "verify": + *e = VERIFY + case "encrypt": + *e = ENCRYPT + case "decrypt": + *e = DECRYPT + case "wrapkey": + *e = WRAPKEY + case "unwrapkey": + *e = UNWRAPKEY + case "derivekey": + *e = DERIVEKEY + default: + return errors.New(`must be one of "Sign", "Verify", "Encrypt", "Decrypt", "WrapKey", "UnwrapKey", "DeriveKey"`) + } + return nil +} + +func (e *KeyUsage) Type() string { + return "Sign|Verify|Encrypt|Decrypt|WrapKey|UnwrapKey|DeriveKey" +} + +type KeyUsageList []KeyUsage + +func (e *KeyUsageList) Set(value string) error { + arr := strings.Split(value, ",") + // in case the user set the usage in the following form: --usage encrypt,decrypt: + // split the input value, and trim spaces + *e = (*e)[:0] // Reset slice content + for i := range arr { + arr[i] = strings.TrimSpace(arr[i]) + } + for _, v := range arr { + var ku KeyUsage + if err := ku.Set(v); err != nil { + return err + } + *e = append(*e, ku) + } + return nil +} + +func (e *KeyUsageList) String() string { + l := make([]string, 0) + for _, ku := range *e { + l = append(l, ku.String()) + } + + return strings.Join(l, ",") +} + +// func (e *KeyUsageList) ToStringArray() []string { +// l := make([]string, 0, len(*e)) +// for _, ku := range *e { +// l = append(l, ku.String()) +// } + +// return l +// } + +func (e *KeyUsageList) ToCryptographicUsageMask() kmip.CryptographicUsageMask { + var mask kmip.CryptographicUsageMask + for _, ku := range *e { + mask |= kmip.CryptographicUsageMask(ku) + } + + return mask +} + +func (e *KeyUsageList) Type() string { + return "Combination of: Sign|Verify|Encrypt|Decrypt|WrapKey|UnwrapKey|DeriveKey" +} diff --git a/common/flagsmgmt/kmipflags/object_type.go b/common/flagsmgmt/kmipflags/object_type.go new file mode 100644 index 0000000..a5db02c --- /dev/null +++ b/common/flagsmgmt/kmipflags/object_type.go @@ -0,0 +1,61 @@ +package kmipflags + +import ( + "errors" + "strings" + + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/ttlv" +) + +type ObjectType kmip.ObjectType + +const ( + ObjectTypeCertificate = ObjectType(kmip.ObjectTypeCertificate) + ObjectTypeSymmetricKey = ObjectType(kmip.ObjectTypeSymmetricKey) + ObjectTypePublicKey = ObjectType(kmip.ObjectTypePublicKey) + ObjectTypePrivateKey = ObjectType(kmip.ObjectTypePrivateKey) + ObjectTypeSplitKey = ObjectType(kmip.ObjectTypeSplitKey) + //nolint:staticcheck // Needed to support legacy templates + ObjectTypeTemplate = ObjectType(kmip.ObjectTypeTemplate) + ObjectTypeSecretData = ObjectType(kmip.ObjectTypeSecretData) + ObjectTypeOpaqueObject = ObjectType(kmip.ObjectTypeOpaqueObject) + ObjectTypePGPKey = ObjectType(kmip.ObjectTypePGPKey) +) + +func (e *ObjectType) String() string { + if *e == 0 { + return "" + } + return ttlv.EnumStr(kmip.ObjectType(*e)) +} + +func (e *ObjectType) Set(v string) error { + switch strings.ToLower(v) { + case "certificate": + *e = ObjectTypeCertificate + case "symmetrickey": + *e = ObjectTypeSymmetricKey + case "publickey": + *e = ObjectTypePublicKey + case "privatekey": + *e = ObjectTypePrivateKey + case "splitkey": + *e = ObjectTypeSplitKey + case "template": + *e = ObjectTypeTemplate + case "secretdata": + *e = ObjectTypeSecretData + case "opaqueobject": + *e = ObjectTypeOpaqueObject + case "pgpkey": + *e = ObjectTypePGPKey + default: + return errors.New(`must be one of "Certificate","SymmetricKey","PublicKey","PrivateKey","SplitKey","Template","SecretData","OpaqueObject","PGPKey"`) + } + return nil +} + +func (e *ObjectType) Type() string { + return "Certificate|SymmetricKey|PublicKey|PrivateKey|SplitKey|Template|SecretData|OpaqueObject|PGPKey" +} diff --git a/common/flagsmgmt/kmipflags/revocation_reason.go b/common/flagsmgmt/kmipflags/revocation_reason.go new file mode 100644 index 0000000..9e1a235 --- /dev/null +++ b/common/flagsmgmt/kmipflags/revocation_reason.go @@ -0,0 +1,51 @@ +package kmipflags + +import ( + "errors" + "strings" + + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/ttlv" +) + +type RevocationReason kmip.RevocationReasonCode + +const ( + Unspecified = RevocationReason(kmip.RevocationReasonCodeUnspecified) + KeyCompromise = RevocationReason(kmip.RevocationReasonCodeKeyCompromise) + CACompromise = RevocationReason(kmip.RevocationReasonCodeCACompromise) + AffiliationChanged = RevocationReason(kmip.RevocationReasonCodeAffiliationChanged) + Superseded = RevocationReason(kmip.RevocationReasonCodeSuperseded) + CessationOfOperation = RevocationReason(kmip.RevocationReasonCodeCessationOfOperation) + PrivilegeWithdrawn = RevocationReason(kmip.RevocationReasonCodePrivilegeWithdrawn) +) + +func (e *RevocationReason) String() string { + return ttlv.EnumStr(kmip.RevocationReasonCode(*e)) +} + +func (e *RevocationReason) Set(v string) error { + switch strings.ToLower(v) { + case "unspecified": + *e = Unspecified + case "keycompromise": + *e = KeyCompromise + case "cacompromise": + *e = CACompromise + case "affiliationchanged": + *e = AffiliationChanged + case "superseded": + *e = Superseded + case "cessationofoperation": + *e = CessationOfOperation + case "privilegewithdrawn": + *e = PrivilegeWithdrawn + default: + return errors.New(`must be one of "Unspecified", "KeyCompromise", "CACompromise", "AffiliationChanged", "Superseded", "CessationOfOperation", "PrivilegeWithdrawn"`) + } + return nil +} + +func (e *RevocationReason) Type() string { + return "Unspecified|KeyCompromise|CACompromise|AffiliationChanged|Superseded|CessationOfOperation|PrivilegeWithdrawn" +} diff --git a/common/flagsmgmt/kmipflags/state.go b/common/flagsmgmt/kmipflags/state.go new file mode 100644 index 0000000..63e7c84 --- /dev/null +++ b/common/flagsmgmt/kmipflags/state.go @@ -0,0 +1,51 @@ +package kmipflags + +import ( + "errors" + "strings" + + "github.com/ovh/kmip-go" + "github.com/ovh/kmip-go/ttlv" +) + +type State kmip.State + +const ( + StatePreActive State = State(kmip.StatePreActive) + StateActive State = State(kmip.StateActive) + StateDeactivated State = State(kmip.StateDeactivated) + StateCompromised State = State(kmip.StateCompromised) + StateDestroyed State = State(kmip.StateDestroyed) + StateDestroyedCompromised State = State(kmip.StateDestroyedCompromised) +) + +func (e *State) String() string { + if *e == 0 { + return "" + } + return ttlv.EnumStr(kmip.State(*e)) +} + +func (e *State) Set(v string) error { + switch strings.ToLower(v) { + case "preactive": + *e = StatePreActive + case "active": + *e = StateActive + case "deactivated": + *e = StateDeactivated + case "compromised": + *e = StateCompromised + case "destroyed": + *e = StateDestroyed + case "destroyedcompromised": + *e = StateDestroyedCompromised + default: + return errors.New(`must be one of "PreActive", "Active", "Deactivated", "Compromised", "Destroyed", "DestroyedCompromised"`) + } + return nil +} + +func (e *State) Type() string { + return "PreActive|Active|Deactivated|Compromised|Destroyed|DestroyedCompromised" +} diff --git a/common/flagsmgmt/restflags/key_usage.go b/common/flagsmgmt/restflags/key_usage.go index f94c0ec..ec93d12 100644 --- a/common/flagsmgmt/restflags/key_usage.go +++ b/common/flagsmgmt/restflags/key_usage.go @@ -44,18 +44,16 @@ func (e *KeyUsageList) Set(value string) error { arr := strings.Split(value, ",") // in case the user set the usage in the following form: --usage encrypt,decrypt: // split the input value, and trim spaces + *e = (*e)[:0] // Reset slice content for i := range arr { arr[i] = strings.TrimSpace(arr[i]) } for _, v := range arr { - switch v { - case "sign", "verify", "encrypt", "decrypt", "wrapKey", "unwrapKey", "deriveKey", "deriveBits": - ku := KeyUsage(v) - *e = append(*e, ku) - - default: - return errors.New(`must be one of "sign", "verify", "encrypt", "decrypt", "wrapKey", "unwrapKey", "deriveKey", "deriveBits"`) + var ku KeyUsage + if err := ku.Set(v); err != nil { + return err } + *e = append(*e, ku) } return nil } diff --git a/doc/okms.md b/doc/okms.md index 1987eb7..ca5aef6 100644 --- a/doc/okms.md +++ b/doc/okms.md @@ -15,6 +15,7 @@ * [okms completion](okms_completion.md) - Generate the autocompletion script for the specified shell * [okms configure](okms_configure.md) - Configure CLI options * [okms keys](okms_keys.md) - Manage domain keys +* [okms kmip](okms_kmip.md) - Manage kmip objects * [okms version](okms_version.md) - Print the version information * [okms x509](okms_x509.md) - Generate, and sign x509 certificates diff --git a/doc/okms_kmip.md b/doc/okms_kmip.md new file mode 100644 index 0000000..d267b7d --- /dev/null +++ b/doc/okms_kmip.md @@ -0,0 +1,37 @@ +## okms kmip + +Manage kmip objects + +### Options + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + -h, --help help for kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) +``` + +### Options inherited from parent commands + +``` + -c, --config string Path to a non default configuration file + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms](okms.md) - +* [okms kmip activate](okms_kmip_activate.md) - Activate an object +* [okms kmip attributes](okms_kmip_attributes.md) - Manage an object's attributes +* [okms kmip create](okms_kmip_create.md) - Create kmip keys +* [okms kmip destroy](okms_kmip_destroy.md) - Destroy an object +* [okms kmip get](okms_kmip_get.md) - Get the materials from a kmip object +* [okms kmip locate](okms_kmip_locate.md) - List kmip objects +* [okms kmip register](okms_kmip_register.md) - Register a kmip object +* [okms kmip rekey](okms_kmip_rekey.md) - Rekey a symmetric key object +* [okms kmip revoke](okms_kmip_revoke.md) - Revoke an object + diff --git a/doc/okms_kmip_activate.md b/doc/okms_kmip_activate.md new file mode 100644 index 0000000..a7e4fc5 --- /dev/null +++ b/doc/okms_kmip_activate.md @@ -0,0 +1,32 @@ +## okms kmip activate + +Activate an object + +``` +okms kmip activate ID [flags] +``` + +### Options + +``` + -h, --help help for activate +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip](okms_kmip.md) - Manage kmip objects + diff --git a/doc/okms_kmip_attributes.md b/doc/okms_kmip_attributes.md new file mode 100644 index 0000000..7192bd7 --- /dev/null +++ b/doc/okms_kmip_attributes.md @@ -0,0 +1,29 @@ +## okms kmip attributes + +Manage an object's attributes + +### Options + +``` + -h, --help help for attributes +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip](okms_kmip.md) - Manage kmip objects +* [okms kmip attributes get](okms_kmip_attributes_get.md) - Get the attributes of an object + diff --git a/doc/okms_kmip_attributes_get.md b/doc/okms_kmip_attributes_get.md new file mode 100644 index 0000000..dcf58c1 --- /dev/null +++ b/doc/okms_kmip_attributes_get.md @@ -0,0 +1,32 @@ +## okms kmip attributes get + +Get the attributes of an object + +``` +okms kmip attributes get ID [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip attributes](okms_kmip_attributes.md) - Manage an object's attributes + diff --git a/doc/okms_kmip_create.md b/doc/okms_kmip_create.md new file mode 100644 index 0000000..7397199 --- /dev/null +++ b/doc/okms_kmip_create.md @@ -0,0 +1,30 @@ +## okms kmip create + +Create kmip keys + +### Options + +``` + -h, --help help for create +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip](okms_kmip.md) - Manage kmip objects +* [okms kmip create key-pair](okms_kmip_create_key-pair.md) - Create an asymmetric key-pair +* [okms kmip create symmetric](okms_kmip_create_symmetric.md) - Create KMIP symmetric key + diff --git a/doc/okms_kmip_create_key-pair.md b/doc/okms_kmip_create_key-pair.md new file mode 100644 index 0000000..ab48b9f --- /dev/null +++ b/doc/okms_kmip_create_key-pair.md @@ -0,0 +1,43 @@ +## okms kmip create key-pair + +Create an asymmetric key-pair + +``` +okms kmip create key-pair [flags] +``` + +### Options + +``` + --alg RSA|ECDSA Key-pair algorithm + --comment string Set the comment attribute on both keys + --curve P-256|P-384|P-521 Elliptic curve for EC keys + --description string Set the description attribute on both keys + -h, --help help for key-pair + --private-extractable Set the extractable attribute on the private key (default true) + --private-name string Optional private key name + --private-sensitive Set sensitive attribute on the private key + --private-usage Combination of: Sign|Verify|Encrypt|Decrypt|WrapKey|UnwrapKey|DeriveKey Private key allowed usage (default Sign) + --public-name string Optional public key name + --public-usage Combination of: Sign|Verify|Encrypt|Decrypt|WrapKey|UnwrapKey|DeriveKey Public key allowed usage (default Verify) + --size int Modulus bit length of the RSA key-pair to generate +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip create](okms_kmip_create.md) - Create kmip keys + diff --git a/doc/okms_kmip_create_symmetric.md b/doc/okms_kmip_create_symmetric.md new file mode 100644 index 0000000..0949a42 --- /dev/null +++ b/doc/okms_kmip_create_symmetric.md @@ -0,0 +1,40 @@ +## okms kmip create symmetric + +Create KMIP symmetric key + +``` +okms kmip create symmetric [flags] +``` + +### Options + +``` + --alg AES|TDES|SKIPJACK Key algorithm + --comment string Set the comment attribute + --description string Set the description attribute + --extractable Set the extractable attribute (default true) + -h, --help help for symmetric + --name string Optional key name + --sensitive Set sensitive attribute + --size int Key bit length + --usage Combination of: Sign|Verify|Encrypt|Decrypt|WrapKey|UnwrapKey|DeriveKey Cryptographic usage (default Encrypt,Decrypt) +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip create](okms_kmip_create.md) - Create kmip keys + diff --git a/doc/okms_kmip_destroy.md b/doc/okms_kmip_destroy.md new file mode 100644 index 0000000..40124c2 --- /dev/null +++ b/doc/okms_kmip_destroy.md @@ -0,0 +1,33 @@ +## okms kmip destroy + +Destroy an object + +``` +okms kmip destroy ID [flags] +``` + +### Options + +``` + --force Force deleton without prompting for confirmation + -h, --help help for destroy +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip](okms_kmip.md) - Manage kmip objects + diff --git a/doc/okms_kmip_get.md b/doc/okms_kmip_get.md new file mode 100644 index 0000000..15c85ac --- /dev/null +++ b/doc/okms_kmip_get.md @@ -0,0 +1,32 @@ +## okms kmip get + +Get the materials from a kmip object + +``` +okms kmip get ID [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip](okms_kmip.md) - Manage kmip objects + diff --git a/doc/okms_kmip_locate.md b/doc/okms_kmip_locate.md new file mode 100644 index 0000000..ee985f2 --- /dev/null +++ b/doc/okms_kmip_locate.md @@ -0,0 +1,35 @@ +## okms kmip locate + +List kmip objects + +``` +okms kmip locate [flags] +``` + +### Options + +``` + --details Display detailed information + -h, --help help for locate + --state PreActive|Active|Deactivated|Compromised|Destroyed|DestroyedCompromised List only object with the given state + --type Certificate|SymmetricKey|PublicKey|PrivateKey|SplitKey|Template|SecretData|OpaqueObject|PGPKey List only objects of the given type +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip](okms_kmip.md) - Manage kmip objects + diff --git a/doc/okms_kmip_register.md b/doc/okms_kmip_register.md new file mode 100644 index 0000000..fed83e0 --- /dev/null +++ b/doc/okms_kmip_register.md @@ -0,0 +1,34 @@ +## okms kmip register + +Register a kmip object + +### Options + +``` + -h, --help help for register +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip](okms_kmip.md) - Manage kmip objects +* [okms kmip register certificate](okms_kmip_register_certificate.md) - Register an X509 certificate +* [okms kmip register key-pair](okms_kmip_register_key-pair.md) - Register a private and a public key objects from private key PEM encoded data +* [okms kmip register private-key](okms_kmip_register_private-key.md) - Register a private key object from PEM encoded data +* [okms kmip register public-key](okms_kmip_register_public-key.md) - Register a public key object from PEM encoded data +* [okms kmip register secret](okms_kmip_register_secret.md) - Register a secret object +* [okms kmip register symmetric](okms_kmip_register_symmetric.md) - Register a symmetric key object + diff --git a/doc/okms_kmip_register_certificate.md b/doc/okms_kmip_register_certificate.md new file mode 100644 index 0000000..3f01d2e --- /dev/null +++ b/doc/okms_kmip_register_certificate.md @@ -0,0 +1,42 @@ +## okms kmip register certificate + +Register an X509 certificate + +### Synopsis + +Register an X509 certificate. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @. + +``` +okms kmip register certificate VALUE [flags] +``` + +### Options + +``` + --comment string Set the comment attribute + --description string Set the description attribute + -h, --help help for certificate + --name string Optional name for the certificate + --pem Certificate is PEM encoded +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip register](okms_kmip_register.md) - Register a kmip object + diff --git a/doc/okms_kmip_register_key-pair.md b/doc/okms_kmip_register_key-pair.md new file mode 100644 index 0000000..40f6e5e --- /dev/null +++ b/doc/okms_kmip_register_key-pair.md @@ -0,0 +1,46 @@ +## okms kmip register key-pair + +Register a private and a public key objects from private key PEM encoded data + +### Synopsis + +Register a private and a public key objects from private key PEM encoded data. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @. + +``` +okms kmip register key-pair VALUE [flags] +``` + +### Options + +``` + --comment string Set the comment attribute on both keys + --description string Set the description attribute on both keys + -h, --help help for key-pair + --private-extractable Set the extractable attribute on the private key (default true) + --private-name string Optional private key name + --private-sensitive Set sensitive attribute on the private key + --private-usage Combination of: Sign|Verify|Encrypt|Decrypt|WrapKey|UnwrapKey|DeriveKey Private key allowed usage (default Sign) + --public-name string Optional public key name + --public-usage Combination of: Sign|Verify|Encrypt|Decrypt|WrapKey|UnwrapKey|DeriveKey Public key allowed usage (default Verify) +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip register](okms_kmip_register.md) - Register a kmip object + diff --git a/doc/okms_kmip_register_private-key.md b/doc/okms_kmip_register_private-key.md new file mode 100644 index 0000000..3ad2ac1 --- /dev/null +++ b/doc/okms_kmip_register_private-key.md @@ -0,0 +1,45 @@ +## okms kmip register private-key + +Register a private key object from PEM encoded data + +### Synopsis + +Register a private key object from PEM encoded data. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @. + +``` +okms kmip register private-key VALUE [flags] +``` + +### Options + +``` + --comment string Set the comment attribute + --description string Set the description attribute + --extractable Set the extractable attribute (default true) + -h, --help help for private-key + --name string Optional name for the key + --public-link string Optional public key ID to link to + --sensitive Set sensitive attribute + --usage Combination of: Sign|Verify|Encrypt|Decrypt|WrapKey|UnwrapKey|DeriveKey Cryptographic usage (default Sign) +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip register](okms_kmip_register.md) - Register a kmip object + diff --git a/doc/okms_kmip_register_public-key.md b/doc/okms_kmip_register_public-key.md new file mode 100644 index 0000000..425a8b3 --- /dev/null +++ b/doc/okms_kmip_register_public-key.md @@ -0,0 +1,43 @@ +## okms kmip register public-key + +Register a public key object from PEM encoded data + +### Synopsis + +Register a public key object from PEM encoded data. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @. + +``` +okms kmip register public-key VALUE [flags] +``` + +### Options + +``` + --comment string Set the comment attribute + --description string Set the description attribute + -h, --help help for public-key + --name string Optional name for the key + --private-link string Optional private key ID to link to + --usage Combination of: Sign|Verify|Encrypt|Decrypt|WrapKey|UnwrapKey|DeriveKey Cryptographic usage (default Verify) +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip register](okms_kmip_register.md) - Register a kmip object + diff --git a/doc/okms_kmip_register_secret.md b/doc/okms_kmip_register_secret.md new file mode 100644 index 0000000..661f15b --- /dev/null +++ b/doc/okms_kmip_register_secret.md @@ -0,0 +1,42 @@ +## okms kmip register secret + +Register a secret object + +### Synopsis + +Register a secret object. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @. + +``` +okms kmip register secret VALUE [flags] +``` + +### Options + +``` + --base64 Given secret is base64 encoded + --comment string Set the comment attribute + --description string Set the description attribute + -h, --help help for secret + --name string Optional name for the secret +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip register](okms_kmip_register.md) - Register a kmip object + diff --git a/doc/okms_kmip_register_symmetric.md b/doc/okms_kmip_register_symmetric.md new file mode 100644 index 0000000..efa0cac --- /dev/null +++ b/doc/okms_kmip_register_symmetric.md @@ -0,0 +1,46 @@ +## okms kmip register symmetric + +Register a symmetric key object + +### Synopsis + +Register a symmetric key object. + +VALUE can be either plain text, a '-' to read from stdin, or a filename prefixed with @. + +``` +okms kmip register symmetric VALUE [flags] +``` + +### Options + +``` + --alg AES|TDES|SKIPJACK Key's cryptographic algorithm + --base64 Given key is base64 encoded + --comment string Set the comment attribute + --description string Set the description attribute + --extractable Set the extractable attribute (default true) + -h, --help help for symmetric + --name string Optional name for the key + --sensitive Set sensitive attribute + --usage Combination of: Sign|Verify|Encrypt|Decrypt|WrapKey|UnwrapKey|DeriveKey Cryptographic usage (default Encrypt,Decrypt) +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip register](okms_kmip_register.md) - Register a kmip object + diff --git a/doc/okms_kmip_rekey.md b/doc/okms_kmip_rekey.md new file mode 100644 index 0000000..2066cd1 --- /dev/null +++ b/doc/okms_kmip_rekey.md @@ -0,0 +1,33 @@ +## okms kmip rekey + +Rekey a symmetric key object + +``` +okms kmip rekey ID [flags] +``` + +### Options + +``` + -h, --help help for rekey + --offset duration Optional rekeying offset +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip](okms_kmip.md) - Manage kmip objects + diff --git a/doc/okms_kmip_revoke.md b/doc/okms_kmip_revoke.md new file mode 100644 index 0000000..963f826 --- /dev/null +++ b/doc/okms_kmip_revoke.md @@ -0,0 +1,35 @@ +## okms kmip revoke + +Revoke an object + +``` +okms kmip revoke ID [flags] +``` + +### Options + +``` + --force Force revoke without prompting for confirmation + -h, --help help for revoke + --message string Optional revocation message + --reason Unspecified|KeyCompromise|CACompromise|AffiliationChanged|Superseded|CessationOfOperation|PrivilegeWithdrawn Revocation reason (default Unspecified) +``` + +### Options inherited from parent commands + +``` + --auth-method mtls Authentication method to use + --ca string Path to CA bundle + --cert string Path to certificate + -c, --config string Path to a non default configuration file + -d, --debug Activate debug mode + --endpoint string Endpoint address to kmip + --key string Path to key file + --output text|json The formatting style for command output. (default text) + --profile string Name of the profile (default "default") +``` + +### SEE ALSO + +* [okms kmip](okms_kmip.md) - Manage kmip objects + diff --git a/go.mod b/go.mod index 13c9d8e..b463890 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/knadh/koanf/providers/file v1.1.2 github.com/knadh/koanf/v2 v2.1.2 github.com/olekukonko/tablewriter v0.0.5 + github.com/ovh/kmip-go v0.2.3 github.com/ovh/okms-sdk-go v0.4.2 github.com/pterm/pterm v0.12.80 github.com/schollz/progressbar/v3 v3.18.0 diff --git a/go.sum b/go.sum index c7a8881..46bb47e 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,8 @@ github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmt github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/ovh/kmip-go v0.2.3 h1:Uvs4D4dyuN3I4MBdAyVWri9Kj/qXWA3kjwjXpBWjFQ4= +github.com/ovh/kmip-go v0.2.3/go.mod h1:ATNCndyULLBcEt7jp4j8cR4ko4gnH37KBA9ZPeNUuKk= github.com/ovh/okms-sdk-go v0.4.2 h1:Vr1HQA0tWoREq5b94Ze2BnG+M1/J87ekWB2/9Cm9wAA= github.com/ovh/okms-sdk-go v0.4.2/go.mod h1:qHignKksvZNNywbHvwJCmy5C6Ro1ZZgNKu2PZO7XTJs= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= diff --git a/tests/kmip.yaml b/tests/kmip.yaml new file mode 100644 index 0000000..59448d8 --- /dev/null +++ b/tests/kmip.yaml @@ -0,0 +1,326 @@ +name: okms-cli KMIP test suite +description: Test the OKMS KMIP subcommand +testcases: + - name: Create keys + steps: + - name: Create an AES 256 key + type: okms-cmd + args: kmip create symmetric --alg AES --size 256 --name test-aes-1 --usage encrypt,decrypt,wrapKey,unwrapKey + assertions: + - result.code ShouldEqual 0 + vars: + aesKeyId: + from: result.systemoutjson.UniqueIdentifier + - name: Create an RSA 2048 key pair + type: okms-cmd + args: kmip create key-pair --alg RSA --size 2048 --private-name test-rsa-1-private --public-name test-rsa-1-public --public-usage verify,encrypt --private-usage sign,decrypt + assertions: + - result.code ShouldEqual 0 + vars: + pubRsaKeyId: + from: result.systemoutjson.PublicKeyUniqueIdentifier + privRsaKeyId: + from: result.systemoutjson.PrivateKeyUniqueIdentifier + - name: Create an ECDSA P-256 key pair + type: okms-cmd + args: kmip create key-pair --alg ECDSA --curve P-256 --private-name test-ecdsa-1-private --public-name test-ecdsa-1-public + assertions: + - result.code ShouldEqual 0 + vars: + pubEcdsaKeyId: + from: result.systemoutjson.PublicKeyUniqueIdentifier + privEcdsaKeyId: + from: result.systemoutjson.PrivateKeyUniqueIdentifier + + - name: Activate object + steps: + - name: Activate AES key + type: okms-cmd + args: kmip activate {{.Create-keys.aesKeyId}} + assertions: + - result.code ShouldEqual 0 + - name: Activate AES key again should fail + type: okms-cmd + args: kmip activate {{.Create-keys.aesKeyId}} + assertions: + - result.code ShouldEqual 1 + + - name: Register then Get secrets + steps: + - name: Register plain secret + type: okms-cmd + args: kmip register secret "foo bar" + assertions: + - result.code ShouldEqual 0 + vars: + secretId: + from: result.systemoutjson.UniqueIdentifier + - name: Get plain secret + type: okms-cmd + args: kmip get {{ .secretId }} + format: text + assertions: + - result.code ShouldEqual 0 + - result.systemout ShouldEqual "foo bar" + - name: Register base64 secret + type: okms-cmd + args: kmip register secret --base64 {{ (b64enc "foo bar") }} + assertions: + - result.code ShouldEqual 0 + vars: + secretId: + from: result.systemoutjson.UniqueIdentifier + - name: Get base64 secret + type: okms-cmd + args: kmip get {{ .secretId }} + format: text + assertions: + - result.code ShouldEqual 0 + - result.systemout ShouldEqual "foo bar" + + - name: Register then Get Symmetric key + steps: + - name: Register plain AES key + type: okms-cmd + args: kmip register symmetric --alg AES "aaaaaaaaaaaaaaaa" + assertions: + - result.code ShouldEqual 0 + vars: + symId: + from: result.systemoutjson.UniqueIdentifier + - name: Get plain AES key + type: okms-cmd + args: kmip get {{ .symId }} + format: text + assertions: + - result.code ShouldEqual 0 + - result.systemout ShouldEqual "aaaaaaaaaaaaaaaa" + - name: Register base64 AES key + type: okms-cmd + args: kmip register symmetric --base64 --alg AES {{(b64enc "aaaaaaaaaaaaaaaa")}} + assertions: + - result.code ShouldEqual 0 + vars: + symId: + from: result.systemoutjson.UniqueIdentifier + - name: Get base64 AES key + type: okms-cmd + args: kmip get {{ .symId }} + format: text + assertions: + - result.code ShouldEqual 0 + - result.systemout ShouldEqual "aaaaaaaaaaaaaaaa" + + - name: Register PrivateKeys + steps: + - name: Register {{ .value.name }} + type: okms-cmd + args: kmip register private-key @{{ .value.file }} + assertions: + - result.code ShouldEqual 0 + range: + - name: ECDSA PKCS8 private key + file: testdata/ecdsa_pkcs8.priv.pem + - name: ECDSA SEC1 private key + file: testdata/ecdsa_sec1.priv.pem + - name: RSA PKCS8 private key + file: testdata/rsa_pkcs8.priv.pem + - name: RSA PKCS1 private key + file: testdata/rsa_pkcs1.priv.pem + + - name: Register PublicKeys + steps: + - name: Register {{ .value.name }} + type: okms-cmd + args: kmip register public-key @{{ .value.file }} + assertions: + - result.code ShouldEqual 0 + range: + - name: ECDSA X509 public key + file: testdata/ecdsa_x509.pub.pem + - name: RSA X509 public key + file: testdata/rsa_x509.pub.pem + - name: ECDSA PKCS8 private key + file: testdata/ecdsa_pkcs8.priv.pem + - name: ECDSA SEC1 private key + file: testdata/ecdsa_sec1.priv.pem + - name: RSA PKCS8 private key + file: testdata/rsa_pkcs8.priv.pem + - name: RSA PKCS1 private key + file: testdata/rsa_pkcs1.priv.pem + + - name: Register Key Pairs + steps: + - name: Register {{ .value.name }} + type: okms-cmd + args: kmip register key-pair @{{ .value.file }} + assertions: + - result.code ShouldEqual 0 + range: + - name: ECDSA PKCS8 private key + file: testdata/ecdsa_pkcs8.priv.pem + - name: ECDSA SEC1 private key + file: testdata/ecdsa_sec1.priv.pem + - name: RSA PKCS8 private key + file: testdata/rsa_pkcs8.priv.pem + - name: RSA PKCS1 private key + file: testdata/rsa_pkcs1.priv.pem + + - name: Register Certificate + steps: + - name: Register X509 certificate + type: okms-cmd + args: kmip register certificate @testdata/x509_cert.pem + assertions: + - result.code ShouldEqual 0 + + - name: Locate kmip objects + steps: + - name: Locate without details + type: okms-cmd + args: kmip locate + assertions: + - result.code ShouldEqual 0 + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.aesKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.pubRsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.privRsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.pubEcdsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.privEcdsaKeyId}} + + - name: Locate with details + type: okms-cmd + args: kmip locate --details + format: text + assertions: + - result.code ShouldEqual 0 + - result.systemout ShouldContainSubstring {{.Create-keys.aesKeyId}} + - result.systemout ShouldContainSubstring {{.Create-keys.pubRsaKeyId}} + - result.systemout ShouldContainSubstring {{.Create-keys.privRsaKeyId}} + - result.systemout ShouldContainSubstring {{.Create-keys.pubEcdsaKeyId}} + - result.systemout ShouldContainSubstring {{.Create-keys.privEcdsaKeyId}} + + - name: Locate by type + type: okms-cmd + args: kmip locate --type PrivateKey + assertions: + - result.code ShouldEqual 0 + - result.systemoutjson.UniqueIdentifier ShouldNotContain {{.Create-keys.aesKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldNotContain {{.Create-keys.pubRsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.privRsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldNotContain {{.Create-keys.pubEcdsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.privEcdsaKeyId}} + + - name: Locate by state + type: okms-cmd + args: kmip locate --state active + assertions: + - result.code ShouldEqual 0 + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.aesKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldNotContain {{.Create-keys.pubRsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldNotContain {{.Create-keys.privRsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldNotContain {{.Create-keys.pubEcdsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldNotContain {{.Create-keys.privEcdsaKeyId}} + + - name: Get attributes + steps: + - name: Get attributes of AES key + type: okms-cmd + args: kmip attributes get {{.Create-keys.aesKeyId}} + format: text + assertions: + - result.code ShouldEqual 0 + - result.systemout ShouldContainSubstring {{.Create-keys.aesKeyId}} + - result.systemout ShouldContainSubstring Active + + - name: Revoke kmip object + steps: + - name: Revoke activated AES key + type: okms-cmd + args: kmip revoke --force {{.Create-keys.aesKeyId}} + assertions: + - result.code ShouldEqual 0 + - name: Revoke activated AES key againe should fail + type: okms-cmd + args: kmip revoke --force {{.Create-keys.aesKeyId}} + assertions: + - result.code ShouldEqual 1 + + - name: Rekey kmip objects + steps: + - name: Rekey AES key + type: okms-cmd + args: kmip rekey {{.Create-keys.aesKeyId}} + assertions: + - result.code ShouldEqual 0 + vars: + newAesKeyId: + from: result.systemoutjson.UniqueIdentifier + - name: Get replacement key attributes + type: okms-cmd + args: kmip attr get {{.newAesKeyId}} + assertions: + - result.code ShouldEqual 0 + + - name: Destroy kmip object + steps: + - name: Destroy revoked object + type: okms-cmd + args: kmip destroy --force {{.Create-keys.aesKeyId}} + assertions: + - result.code ShouldEqual 0 + - name: Destroy a destroyed object should fail + type: okms-cmd + args: kmip destroy --force {{.Create-keys.aesKeyId}} + assertions: + - result.code ShouldEqual 1 + - name: Destroy PreActive object + type: okms-cmd + args: kmip destroy --force {{.Create-keys.pubRsaKeyId}} + assertions: + - result.code ShouldEqual 0 + - name: Locate without details + type: okms-cmd + args: kmip locate + assertions: + - result.code ShouldEqual 0 + - result.systemoutjson.UniqueIdentifier ShouldNotContain {{.Create-keys.aesKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldNotContain {{.Create-keys.pubRsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.privRsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.pubEcdsaKeyId}} + - result.systemoutjson.UniqueIdentifier ShouldContain {{.Create-keys.privEcdsaKeyId}} + + - name: Cleanup Domain + steps: + # - name: List all active keys + # type: okms-cmd + # args: kmip ls --state active + # assertions: + # - result.code ShouldEqual 0 + # vars: + # allKeys: + # from: result.systemoutjson.UniqueIdentifier + + # - name: Revoke key {{ .value }} + # type: okms-cmd + # skip: + # - allKeys ShouldBeNil + # range: "{{ .allKeys }}" + # args: kmip revoke {{ .value }} --force + # assertions: + # - result.code ShouldEqual 0 + + - name: List all keys + type: okms-cmd + args: kmip ls + assertions: + - result.code ShouldEqual 0 + vars: + allKeys: + from: result.systemoutjson.UniqueIdentifier + + - name: Force delete {{ .value }} + type: okms-cmd + range: "{{.allKeys}}" + args: kmip delete {{ .value }} --force + assertions: + - result.code ShouldEqual 0 diff --git a/tests/testdata/ecdsa_x509.pub.pem b/tests/testdata/ecdsa_x509.pub.pem new file mode 100644 index 0000000..f850040 --- /dev/null +++ b/tests/testdata/ecdsa_x509.pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpsCHdH3DlkOg57JMzA1aroeRRKi3 +leklvZB7OxTlu7drvNn5pA5dM/19y9g9iGf2mhqToN6kACpL4GAjtuhxpg== +-----END PUBLIC KEY----- diff --git a/tests/testdata/rsa_x509.pub.pem b/tests/testdata/rsa_x509.pub.pem new file mode 100644 index 0000000..9f077d2 --- /dev/null +++ b/tests/testdata/rsa_x509.pub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsTqTVNZRbTR97N8NUlLm +1dLPGyyamB1+DRBoyT5mPB7YsuGiNqpn9npjPpm1AC8k2PLxmo5p6XAx/SnBAGNm +/8lfRed2eQvGeBQslomFyrrTvbz/sU1++SBo38zKzgCQBqgmmLyAN4/9/saUstRX +hIK4xiokrfIvUkmKIG5NgeBKikGSX8yvf22z1U8CUWy6gvaanNGWS56eWplUkyK5 +apsxxDB3VAes2NWFH5RLxkr44Yq1MK3PMQTuIXqhm9qbVADQ/+qFVMBH8I+8Srdr +hvd8hTDcgTbAKs9PPHTfrdWG7I+G0QYSEykoZZM8JegabEgJcfzYnI8TA+hP8dRg +EQIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/testdata/x509_cert.pem b/tests/testdata/x509_cert.pem new file mode 100644 index 0000000..bc779fb --- /dev/null +++ b/tests/testdata/x509_cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB4DCCAYagAwIBAgIRALqcnTPYAELvhhhut4CT4mkwCgYIKoZIzj0EAwIwFTET +MBEGA1UEAxMKQ0NNVXNlcnNDQTAeFw0yNTAyMDQwOTA5NDlaFw0yNjAyMDQwOTA5 +NDlaMA8xDTALBgNVBAMTBG9rbXMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATEXdel +pCcrQM4gTgZtxxo1v5ujB4kd4fBA3xR1Esw8Y6UdmQQJa7N92zk9xMrO8cKWGRBs +G0mQ+TMYN6S8YrE8bscNqxmEr5YofSAUFeMfWjBs52S/p9gb2w5qUKjc5+ejgZ8w +gZwwCQYDVR0TBAIwADALBgNVHQ8EBAMCA8gwHQYDVR0OBBYEFHto0DlBsw0Xbjny +QXvBsohDY1ToMBMGA1UdJQQMMAoGCCsGAQUFBwMCME4GA1UdEQEB/wREMEKgQAYK +KwYBBAGCNxQCA6AyDDBva21zLmRvbWFpbjo2MDQwNjY1OS02ZGMzLTQ0NDctODZm +Ny0xNDc1NzU4MGU0ZjgwCgYIKoZIzj0EAwIDSAAwRQIgPJW9jCmb0kfEE1X27Ngt +Hzu7ZvBPTSqN4TRkf+xkBCECIQDesSk9/XDRxvS1QZhh0HywhDgoFYP6UtRR4T+d +y4D+wQ== +-----END CERTIFICATE----- \ No newline at end of file