From efcc51f24ece101ce73da05fb5e85a11926149f7 Mon Sep 17 00:00:00 2001 From: like Date: Wed, 17 May 2023 10:10:51 +0800 Subject: [PATCH] init project --- .gitignore | 120 +++++++++++++++++++++++++++++-- .idea/.gitignore | 8 +++ .idea/go-license.iml | 9 +++ .idea/modules.xml | 8 +++ .idea/vcs.xml | 6 ++ cmd/license-create/main.go | 56 ++++++++------- cmd/license-keygen/main.go | 16 ++--- cmd/license-keyinit/main.go | 6 ++ cmd/license-renew/main.go | 16 +++-- cmd/license-server/main.go | 22 ++++-- cmd/license-verify/main.go | 41 +++++++++-- go.mod | 8 +++ go.sum | 10 +++ license/licenseutil/util.go | 24 ++++++- main.go | 139 ++++++++++++++++++++++++++++++++++++ 15 files changed, 428 insertions(+), 61 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/go-license.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 cmd/license-keyinit/main.go create mode 100644 main.go diff --git a/.gitignore b/.gitignore index efbe8a8..c885fc6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,118 @@ -*.out -*.txt -id_ed25519 -id_ed25519.pub +cmd/license-keyinit/id_ed25519 +cmd/license-keyinit/id_ed25519.pub license.txt !license/licenseutil/testdata/* + +### VisualStudioCode template +.vscode/* +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +# User-specific stuff +# Generated files +# Sensitive or high-churn files +# Gradle +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# *.iml +# *.ipr +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +# File-based project format +*.iws + +# IntelliJ +out/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +# Android studio 3.1+ serialized cache file +### Go template +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +# Test binary, built with `go test -c` +*.test +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +# Dependency directories (remove the comment below to include it) +# vendor/ +vendor/ + +### Vim template +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ + +# Auto-generated tag files +tags + +# Persistent undo +[._]*.un~ + +### Archives template +# It's better to unpack these files and commit the raw source because +# git has its own built in compression methods. +*.7z +*.jar +*.rar +*.zip +*.gz +*.gzip +*.tgz +*.bzip +*.bzip2 +*.bz2 +*.xz +*.lzma +*.cab +*.xar + +# Packing-only formats +*.iso +*.tar + +# Package management formats +*.dmg +*.xpi +*.gem +*.egg +*.deb +*.rpm +*.msi +*.msm +*.msp +*.txz diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/go-license.iml b/.idea/go-license.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/go-license.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..8d716ba --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/cmd/license-create/main.go b/cmd/license-create/main.go index bb411f8..a16bc29 100644 --- a/cmd/license-create/main.go +++ b/cmd/license-create/main.go @@ -2,12 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package cmd import ( - "flag" "fmt" - "io/ioutil" "os" "time" @@ -15,36 +13,40 @@ import ( "github.com/drone/go-license/license/licenseutil" ) -var month = time.Hour * 24 * 31 - -var ( - in = flag.String("in", "id_ed25519", "") - out = flag.String("out", "license.txt", "") - iss = flag.String("iss", "", "") - cus = flag.String("cus", "", "") - sub = flag.String("sub", "", "") - typ = flag.String("typ", "", "") - lim = flag.Int("lim", 0, "") - exp = flag.Duration("exp", month, "") -) - -func main() { - flag.Parse() +var year = time.Hour * 24 * 365 + +type Args struct { + In string `json:"in"` // 输入数据 + Out string `json:"out"` // 输出数据 + Iss string `json:"iss"` // 发布者 + Cus string `json:"cus"` // 客户 + Sub string `json:"sub"` // 主题 + Typ string `json:"typ"` // 类型 + Lim int `json:"lim"` // 限制 + Dat string `json:"dat"` // 数据 + Exp time.Duration `json:"exp"` // 过期时间 +} - privateKey, err := licenseutil.ReadPrivateKey(*in) +func LicenseCreate(args Args) { + if args.Exp == 0 { + args.Exp = year + } + privateKey, err := licenseutil.ReadPrivateKey(args.In) if err != nil { fmt.Println(err) os.Exit(1) } + now := time.Now().UTC() license := &license.License{ - Iss: *iss, - Cus: *cus, - Sub: *sub, - Typ: *typ, - Lim: *lim, - Exp: time.Now().UTC().Add(*exp), - Iat: time.Now().UTC(), + Iss: args.Iss, + Cus: args.Cus, + Sub: args.Sub, + Typ: args.Typ, + Lim: args.Lim, + Exp: now.Add(args.Exp), + Iat: now, + Dat: []byte(args.Dat), } encoded, err := license.Encode(privateKey) @@ -53,7 +55,7 @@ func main() { os.Exit(1) } - err = ioutil.WriteFile(*out, encoded, 0644) + err = os.WriteFile(args.Out, encoded, 0644) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/license-keygen/main.go b/cmd/license-keygen/main.go index 93d37ac..7111dfc 100644 --- a/cmd/license-keygen/main.go +++ b/cmd/license-keygen/main.go @@ -2,24 +2,24 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package cmd import ( "crypto/rand" "encoding/base64" - "flag" "fmt" - "io/ioutil" "os" "golang.org/x/crypto/ed25519" ) -var out = flag.String("out", "id_ed25519", "") +// var out = flag.String("out", "id_ed25519", "") -func main() { - flag.Parse() +type Args struct { + Out string +} +func LicenseKeyGen(args Args) { publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { fmt.Println(err) @@ -29,13 +29,13 @@ func main() { publicKeyHex, privateKeyHex := base64.StdEncoding.EncodeToString(publicKey), base64.StdEncoding.EncodeToString(privateKey) - err = ioutil.WriteFile(*out, []byte(privateKeyHex), 0644) + err = os.WriteFile(args.Out, []byte(privateKeyHex), 0644) if err != nil { fmt.Println(err) os.Exit(1) } - err = ioutil.WriteFile(*out+".pub", []byte(publicKeyHex), 0644) + err = os.WriteFile(args.Out+".pub", []byte(publicKeyHex), 0644) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/license-keyinit/main.go b/cmd/license-keyinit/main.go new file mode 100644 index 0000000..bb6cd52 --- /dev/null +++ b/cmd/license-keyinit/main.go @@ -0,0 +1,6 @@ +package license_keyinit + +import "embed" + +//go:embed id_ed25519 id_ed25519.pub +var Files embed.FS diff --git a/cmd/license-renew/main.go b/cmd/license-renew/main.go index f73bf0f..7270db7 100644 --- a/cmd/license-renew/main.go +++ b/cmd/license-renew/main.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package cmd import ( "bytes" @@ -23,16 +23,20 @@ var ( addr = flag.String("addr", "http://localhost:9000", "") ) -func main() { - flag.Parse() +type Args struct { + Pub string + File string + Addr string +} - publicKey, err := licenseutil.ReadPublicKey(*pub) +func LicenseRenew(args Args) { + publicKey, err := licenseutil.ReadPublicKey(args.Pub) if err != nil { fmt.Println(err) os.Exit(1) } - l, err := license.DecodeFile(*file, publicKey) + l, err := license.DecodeFile(args.File, publicKey) if err != nil { fmt.Println(err) os.Exit(1) @@ -41,7 +45,7 @@ func main() { buf := new(bytes.Buffer) json.NewEncoder(buf).Encode(l) - res, err := http.Post(*addr, "application/json", buf) + res, err := http.Post(args.Addr, "application/json", buf) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/license-server/main.go b/cmd/license-server/main.go index d28f95c..5cbdc64 100644 --- a/cmd/license-server/main.go +++ b/cmd/license-server/main.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package cmd import ( "encoding/json" @@ -16,17 +16,25 @@ import ( var monthly = time.Hour * 24 * 30 -var ( +/*var ( addr = flag.String("addr", ":9000", "") renewal = flag.Duration("dur", monthly, "") signer = flag.String("signer", "id_ed25519", "") -) +)*/ + +var Payload Args + +type Args struct { + Addr string + Renewal time.Duration + Signer string +} -func main() { +func LicenseServer() { flag.Parse() http.HandleFunc("/", handler) - http.ListenAndServe(*addr, nil) + http.ListenAndServe(Payload.Addr, nil) } func handler(w http.ResponseWriter, r *http.Request) { @@ -40,9 +48,9 @@ func handler(w http.ResponseWriter, r *http.Request) { // TODO verify the License.Cus (customer id) is valid // and in good standing prior to issuing a new license. - l.Exp = time.Now().UTC().Add(*renewal) + l.Exp = time.Now().UTC().Add(Payload.Renewal) - privateKey, err := licenseutil.ReadPrivateKey(*signer) + privateKey, err := licenseutil.ReadPrivateKey(Payload.Signer) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/cmd/license-verify/main.go b/cmd/license-verify/main.go index 3770d4b..3d004ae 100644 --- a/cmd/license-verify/main.go +++ b/cmd/license-verify/main.go @@ -2,33 +2,39 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package cmd import ( "encoding/json" "flag" "fmt" "os" + "time" "github.com/drone/go-license/license" "github.com/drone/go-license/license/licenseutil" ) -var ( +/*var ( pub = flag.String("pub", "id_ed25519.pub", "") file = flag.String("file", "license.txt", "") -) +)*/ + +type Args struct { + Pub string + File string +} -func main() { +func LicenseVerify(args Args) { flag.Parse() - publicKey, err := licenseutil.ReadPublicKey(*pub) + publicKey, err := licenseutil.ReadPublicKey(args.Pub) if err != nil { fmt.Println(err) os.Exit(1) } - l, err := license.DecodeFile(*file, publicKey) + l, err := license.DecodeFile(args.File, publicKey) if err != nil { fmt.Println(err) os.Exit(1) @@ -38,3 +44,26 @@ func main() { enc.SetIndent("", " ") enc.Encode(l) } + +func LicenseHealth(args Args) { + flag.Parse() + + publicKey, err := licenseutil.ReadPublicKey(args.Pub) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + l, err := license.DecodeFile(args.File, publicKey) + if err == nil { + if l.Exp.After(time.Now()) { + os.Exit(0) // 正常退出 + } + os.Exit(1) + } + + if err != nil { + fmt.Println(err) + os.Exit(2) + } +} diff --git a/go.mod b/go.mod index 2e36bee..8b455aa 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,11 @@ module github.com/drone/go-license +go 1.19 + require golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect +) diff --git a/go.sum b/go.sum index a6d0dc6..bdd6c8f 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,12 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac h1:7d7lG9fHOLdL6jZPtnV4LpI41SbohIJ1Atq7U991dMg= golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/license/licenseutil/util.go b/license/licenseutil/util.go index 489a4b1..66efcd6 100644 --- a/license/licenseutil/util.go +++ b/license/licenseutil/util.go @@ -9,14 +9,24 @@ package licenseutil import ( "encoding/base64" - "io/ioutil" + license_keyinit "github.com/drone/go-license/cmd/license-keyinit" + "io/fs" + "os" "golang.org/x/crypto/ed25519" ) // ReadPublicKey reads a base64-encoded public key file. func ReadPublicKey(path string) (ed25519.PublicKey, error) { - data, err := ioutil.ReadFile(path) + var ( + data []byte + err error + ) + if path == "empty" { + data, _ = fs.ReadFile(license_keyinit.Files, "id_ed25519.pub") + } else { + data, err = os.ReadFile(path) + } if err != nil { return nil, err } @@ -25,7 +35,15 @@ func ReadPublicKey(path string) (ed25519.PublicKey, error) { // ReadPrivateKey reads a base64-encoded private key file. func ReadPrivateKey(path string) (ed25519.PrivateKey, error) { - data, err := ioutil.ReadFile(path) + var ( + data []byte + err error + ) + if path == "empty" { + data, _ = fs.ReadFile(license_keyinit.Files, "id_ed25519") + } else { + data, err = os.ReadFile(path) + } if err != nil { return nil, err } diff --git a/main.go b/main.go new file mode 100644 index 0000000..8e6753a --- /dev/null +++ b/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "fmt" + create "github.com/drone/go-license/cmd/license-create" + keygen "github.com/drone/go-license/cmd/license-keygen" + renew "github.com/drone/go-license/cmd/license-renew" + server "github.com/drone/go-license/cmd/license-server" + verify "github.com/drone/go-license/cmd/license-verify" + "github.com/spf13/cobra" + "os" + "time" +) + +var ( + licenseKeyGenArgs keygen.Args + licenseCreateArgs create.Args + licenseRenewArgs renew.Args + licenseVerifyArgs verify.Args +) + +var rootCmd = &cobra.Command{ + Use: "go-license", + Short: "go license tool", + Long: `This tool is used to generate ed25519 signed license`, +} + +var keygenCmd = &cobra.Command{ + Use: "keygen --out=id_ed25519", + Short: "Output license public and private keys", + Args: cobra.MaximumNArgs(1), + DisableFlagsInUseLine: true, + Run: func(cmd *cobra.Command, args []string) { + keygen.LicenseKeyGen(licenseKeyGenArgs) + }, +} + +var createCmd = &cobra.Command{ + Use: "create --iss=company_name --cus=user_name --sub=user_id --typ=internal --exp=12h", + Short: "Create a certificate signed with ed25519", + Long: `Create a certificate signed with ed25519`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("args:%+v\n", licenseCreateArgs) + create.LicenseCreate(licenseCreateArgs) + }, +} + +var renewCmd = &cobra.Command{ + Use: "renew --pub=public_key --file=file.txt --addr=http://localhost:9000", + Short: "Renew a license", + Long: `Renew a license`, + //Args: cobra.ExactArgs(0), + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("args:%+v\n", licenseRenewArgs) + renew.LicenseRenew(licenseRenewArgs) + }, +} + +var serverCmd = &cobra.Command{ + Use: "server --addr=:9000 --renewal=30d --signer=id_ed25519", + Short: "Start the server", + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("The service listens on:%s signer:%s renewal:%s\n", server.Payload.Addr, server.Payload.Signer, server.Payload.Renewal) + server.LicenseServer() + }, +} + +var verifyCmd = &cobra.Command{ + Use: "verify --pub=id_ed25519.pub --file=license.txt", + Short: "Validate license and return the plaintext license content", + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("args:%+v\n", licenseVerifyArgs) + verify.LicenseVerify(licenseVerifyArgs) + }, +} + +var healthCmd = &cobra.Command{ + Use: "health --pub=id_ed25519.pub --file=license.txt", + Short: "Validate license and return the plaintext license content", + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("args:%+v\n", licenseVerifyArgs) + verify.LicenseHealth(licenseVerifyArgs) + }, +} + +/* + privateKey, _ := fs.ReadFile(Files, "id_ed25519") + publicKey, _ := fs.ReadFile(Files, "id_ed25519.pub") + fmt.Println(string(privateKey)) + fmt.Println(string(publicKey)) +*/ + +func init() { + + keygenCmd.Flags().StringVarP(&licenseKeyGenArgs.Out, "out", "", "id_ed25519", "license file name") + rootCmd.AddCommand(keygenCmd) + + createCmd.Flags().StringVarP(&licenseCreateArgs.In, "in", "", "empty", "Input data") // 兼容内容:去除默认值,在函数中判断,id_ed25519 + createCmd.Flags().StringVarP(&licenseCreateArgs.Out, "out", "", "license.txt", "license file") + createCmd.Flags().StringVarP(&licenseCreateArgs.Iss, "iss", "", "", "Issuer") + createCmd.Flags().StringVarP(&licenseCreateArgs.Cus, "cus", "", "", "Customer") + createCmd.Flags().StringVarP(&licenseCreateArgs.Sub, "sub", "", "", "Subject") + createCmd.Flags().StringVarP(&licenseCreateArgs.Typ, "typ", "", "", "Type") + createCmd.Flags().StringVarP(&licenseCreateArgs.Dat, "dat", "", "", "JSON data") + createCmd.Flags().IntVarP(&licenseCreateArgs.Lim, "lim", "", 0, "Limit") + createCmd.Flags().DurationVarP(&licenseCreateArgs.Exp, "exp", "", 0, "Expiration time") + rootCmd.AddCommand(createCmd) + + renewCmd.Flags().StringVarP(&licenseRenewArgs.Pub, "pub", "", "empty", "Public key") // id_ed25519.pub + renewCmd.Flags().StringVarP(&licenseRenewArgs.File, "file", "", "license.txt", "File name") + renewCmd.Flags().StringVarP(&licenseRenewArgs.Addr, "addr", "", "http://localhost:9000", "Ip Address") + rootCmd.AddCommand(renewCmd) + + serverCmd.Flags().StringVarP(&server.Payload.Addr, "addr", "", ":9000", "Address to bind to") + serverCmd.Flags().DurationVarP(&server.Payload.Renewal, "renewal", "", time.Hour*24*30, "Certificate renewal interval in seconds") + serverCmd.Flags().StringVarP(&server.Payload.Signer, "signer", "", "empty", "Certificate signer") // id_ed25519 + rootCmd.AddCommand(serverCmd) + + verifyCmd.Flags().StringVarP(&licenseVerifyArgs.Pub, "pub", "", "empty", "Public key file path") //id_ed25519.pub + verifyCmd.Flags().StringVarP(&licenseVerifyArgs.File, "file", "", "license.txt", "File to verify") + rootCmd.AddCommand(verifyCmd) + + healthCmd.Flags().StringVarP(&licenseVerifyArgs.Pub, "pub", "", "empty", "Public key file path") //id_ed25519.pub + healthCmd.Flags().StringVarP(&licenseVerifyArgs.File, "file", "", "license.txt", "File to verify") + rootCmd.AddCommand(healthCmd) + + /*privateKey, _ := fs.ReadFile(license_keyinit.Files, "id_ed25519") + publicKey, _ := fs.ReadFile(license_keyinit.Files, "id_ed25519.pub") + fmt.Println("privateKey", string(privateKey)) + fmt.Println("publicKey", string(publicKey))*/ + +} + +func main() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} -- 2.26.0