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