first
This commit is contained in:
commit
14831e38c8
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
.idea
|
||||
|
||||
log
|
||||
vendor
|
||||
/config.yml
|
||||
acme-mana.exe
|
||||
acme-mana.pid
|
||||
acme-mana.sock
|
8
go.mod
Normal file
8
go.mod
Normal file
|
@ -0,0 +1,8 @@
|
|||
module acme-client
|
||||
|
||||
go 1.23.2
|
||||
|
||||
require (
|
||||
github.com/go-acme/lego/v4 v4.19.2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
12
go.sum
Normal file
12
go.sum
Normal file
|
@ -0,0 +1,12 @@
|
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-acme/lego/v4 v4.19.2 h1:Y8hrmMvWETdqzzkRly7m98xtPJJivWFsgWi8fcvZo+Y=
|
||||
github.com/go-acme/lego/v4 v4.19.2/go.mod h1:wtDe3dDkmV4/oI2nydpNXSJpvV10J9RCyZ6MbYxNtlQ=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
122
main.go
Normal file
122
main.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"acme-client/src"
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 读取server参数,如果没有提示添加
|
||||
config := src.GetClientConfig()
|
||||
server := config.Server
|
||||
if server == "" {
|
||||
log.Println("请输入服务端地址; 如: http://acme.server.com:8080")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
serverAddr, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
serverAddr = strings.Trim(serverAddr, "\r\n")
|
||||
config.Server = serverAddr
|
||||
src.WriteConfig()
|
||||
}
|
||||
|
||||
// 读取服务器RSA公钥, 没有提示添加
|
||||
rsaPublicKey := config.RsaPublicKey
|
||||
if rsaPublicKey == "" {
|
||||
log.Println("请输入服务端下发的RAS公钥;")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
rsaPublicKeyContent, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rsaPublicKeyContent = strings.Trim(rsaPublicKeyContent, "\r\n")
|
||||
config.RsaPublicKey = rsaPublicKeyContent
|
||||
src.WriteConfig()
|
||||
}
|
||||
|
||||
// 获取指令, 如果没有则提示添加
|
||||
args := os.Args
|
||||
if len(args) < 2 {
|
||||
log.Println("请输入命令; 如: add")
|
||||
log.Println("或者输入 help 查看帮助")
|
||||
return
|
||||
}
|
||||
onCommand()
|
||||
}
|
||||
|
||||
func onCommand() {
|
||||
command := os.Args[1]
|
||||
switch command {
|
||||
case "help":
|
||||
showHelp()
|
||||
case "-s":
|
||||
onServerCommand()
|
||||
default:
|
||||
log.Fatalf("Unknown command: %s", command)
|
||||
}
|
||||
}
|
||||
|
||||
func showHelp() {
|
||||
log.Printf("help\t查看帮助")
|
||||
log.Printf("list\t查看配置列表")
|
||||
log.Printf("add\t添加配置")
|
||||
log.Printf("del\t删除配置")
|
||||
log.Printf("edit\t编辑配置")
|
||||
log.Printf("-s list\t查看服务端已配置的Domain名称")
|
||||
}
|
||||
func showList() {
|
||||
for i := range src.GetClientConfig().Domains {
|
||||
domain := src.GetClientConfig().Domains[i]
|
||||
log.Printf("- %s", domain.Name)
|
||||
log.Printf(" - cert: %s", domain.CertFile)
|
||||
log.Printf(" - key: %s", domain.KeyFile)
|
||||
log.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func onServerCommand() {
|
||||
args := os.Args
|
||||
if len(args) < 3 {
|
||||
log.Fatal("参数错误, 请检查")
|
||||
}
|
||||
command := args[2]
|
||||
switch command {
|
||||
case "list":
|
||||
showServerList()
|
||||
default:
|
||||
log.Fatal("参数错误, 请检查")
|
||||
}
|
||||
}
|
||||
|
||||
func showServerList() {
|
||||
server := src.GetClientConfig().Server
|
||||
token, encryptToken := src.GenToken()
|
||||
url := server + "/api/v1/domain/list?token=" + encryptToken
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
result := src.Result{}
|
||||
err = json.Unmarshal(body, &result)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if result.Code != 200 {
|
||||
log.Fatal("读取数据出错; ", result.Msg)
|
||||
}
|
||||
data := result.Data
|
||||
text := src.DecryptByToken(token, data)
|
||||
println(text)
|
||||
}
|
106
src/config.go
Normal file
106
src/config.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package src
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
)
|
||||
|
||||
func ReadConfig() ClientConfig {
|
||||
InitConfig()
|
||||
file, err := os.ReadFile(getConfigFile())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var conf ClientConfig
|
||||
err = yaml.Unmarshal(file, &conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
func InitConfig() {
|
||||
confFile := getConfigFile()
|
||||
_, err := os.Stat(confFile)
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
log.Infof("客户端配置文件不存在,自动创建默认配置文件")
|
||||
conf := defaultConfig()
|
||||
file, err := os.Create(confFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
data, err := yaml.Marshal(conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = file.Write(data)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func FlushConfig() {
|
||||
ReadConfig()
|
||||
}
|
||||
|
||||
func WriteConfig() {
|
||||
confFile := getConfigFile()
|
||||
data, err := yaml.Marshal(GetClientConfig())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = os.WriteFile(confFile, data, 0755)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func defaultConfig() *ClientConfig {
|
||||
return &ClientConfig{
|
||||
Server: "",
|
||||
RsaPublicKey: "",
|
||||
}
|
||||
}
|
||||
|
||||
func getConfigFile() string {
|
||||
return env.GetOrDefaultString(ENV_CLIENT_CONF_FILE, "config.yml")
|
||||
}
|
||||
|
||||
const ENV_CLIENT_CONF_FILE = "ACME_MANA_CONF_FILE"
|
||||
|
||||
type ClientConfig struct {
|
||||
Server string
|
||||
RsaPublicKey string
|
||||
Domains []Domain
|
||||
}
|
||||
|
||||
type Domain struct {
|
||||
Name string
|
||||
CertFile string
|
||||
KeyFile string
|
||||
}
|
||||
|
||||
func (conf *ClientConfig) FindDomain(name string) *Domain {
|
||||
for _, domain := range conf.Domains {
|
||||
if domain.Name == name {
|
||||
return &domain
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//func (conf *ClientConfig) SetServer(server string) *ClientConfig {
|
||||
// conf.Server = server
|
||||
// WriteConfig()
|
||||
// return conf
|
||||
//}
|
||||
//func (conf *ClientConfig) SetPubKey(pubkey string) *ClientConfig {
|
||||
// conf.RsaPublicKey = pubkey
|
||||
// WriteConfig()
|
||||
// return conf
|
||||
//}
|
96
src/crypto/crypto.go
Normal file
96
src/crypto/crypto.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
)
|
||||
|
||||
func EncryptAES(key []byte, plaintext []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
func DecryptAES(key []byte, ciphertext []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(ciphertext) < aes.BlockSize {
|
||||
panic("ciphertext too short")
|
||||
}
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
ciphertext = ciphertext[aes.BlockSize:]
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext, ciphertext)
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
func EncryptRSA(publicKey *rsa.PublicKey, message []byte) ([]byte, error) {
|
||||
ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func EncryptRSABase64(publicKey string, content []byte) ([]byte, error) {
|
||||
pubKey, err := base64.StdEncoding.DecodeString(publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := x509.ParsePKCS1PublicKey(pubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return EncryptRSA(key, content)
|
||||
}
|
||||
|
||||
func DecryptRSA(privateKey *rsa.PrivateKey, ciphertext []byte) ([]byte, error) {
|
||||
plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func DecryptRSABase64(privateKey string, ciphertext []byte) ([]byte, error) {
|
||||
priKey, err := base64.StdEncoding.DecodeString(privateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := x509.ParsePKCS1PrivateKey(priKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return DecryptRSA(key, ciphertext)
|
||||
}
|
||||
|
||||
func GenRSA() (priKey string, pubKey string, err error) {
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
priKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
|
||||
|
||||
publicKey := &privateKey.PublicKey
|
||||
publicKeyBytes := x509.MarshalPKCS1PublicKey(publicKey)
|
||||
pubKey = base64.StdEncoding.EncodeToString(publicKeyBytes)
|
||||
priKey = base64.StdEncoding.EncodeToString(priKeyBytes)
|
||||
err = nil
|
||||
return
|
||||
}
|
76
src/token.go
Normal file
76
src/token.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
package src
|
||||
|
||||
import (
|
||||
"acme-client/src/crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
func randomStr(length int) string {
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
randomIndex, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
|
||||
b[i] = charset[randomIndex.Int64()]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func GenToken() (token string, encryptToken string) {
|
||||
token = randomStr(32)
|
||||
key := GetClientConfig().RsaPublicKey
|
||||
|
||||
tokenBytes, err := crypto.EncryptRSABase64(key, []byte(token))
|
||||
if err != nil {
|
||||
log.Fatal("Error encrypting data:", err)
|
||||
}
|
||||
//encryptToken = base64.StdEncoding.EncodeToString(tokenBytes)
|
||||
encryptToken = hex.EncodeToString(tokenBytes)
|
||||
|
||||
//keyBytes, err := base64.StdEncoding.DecodeString(key)
|
||||
//if err != nil {
|
||||
// log.Fatal("Error decoding public key:", err)
|
||||
//}
|
||||
//pubKey, err := x509.ParsePKCS1PublicKey(keyBytes)
|
||||
//if err != nil {
|
||||
// log.Fatal("Error parsing public key:", err)
|
||||
//}
|
||||
//
|
||||
//enToken, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, pubKey, []byte(token), nil)
|
||||
//if err != nil {
|
||||
// log.Fatal("Error encrypting data:", err)
|
||||
//}
|
||||
//encryptToken = base64.StdEncoding.EncodeToString(enToken)
|
||||
return
|
||||
}
|
||||
|
||||
func DecryptByToken(token string, data string) string {
|
||||
// 使用 token 进行 DES 解密
|
||||
key := []byte(token)
|
||||
dataBytes, err := base64.StdEncoding.DecodeString(data)
|
||||
if err != nil {
|
||||
log.Fatal("Error decoding data:", err)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
log.Fatal("Error creating cipher:", err)
|
||||
}
|
||||
|
||||
if len(dataBytes) < aes.BlockSize {
|
||||
log.Fatal("ciphertext too short")
|
||||
}
|
||||
|
||||
iv := dataBytes[:aes.BlockSize]
|
||||
dataBytes = dataBytes[aes.BlockSize:]
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(dataBytes, dataBytes)
|
||||
return string(dataBytes)
|
||||
}
|
17
src/variable.go
Normal file
17
src/variable.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package src
|
||||
|
||||
var clientConfig ClientConfig = ReadConfig()
|
||||
|
||||
func GetClientConfig() *ClientConfig {
|
||||
return &clientConfig
|
||||
}
|
||||
|
||||
func ReloadClientConfig() {
|
||||
clientConfig = ReadConfig()
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Code int
|
||||
Msg string
|
||||
Data string
|
||||
}
|
Loading…
Reference in New Issue
Block a user