浏览代码

encrypt Password and SecretKey (#19)

* feat: aes-utils

* feat: encrypt Password and SecretKey

Co-authored-by: jeessy2 <[email protected]>
jeessy2 3 年之前
父节点
当前提交
74190fa8d4
共有 8 个文件被更改,包括 200 次插入4 次删除
  1. 1 0
      entity/config.go
  2. 13 2
      entity/s3_config.go
  3. 82 0
      util/aes_util.go
  4. 34 0
      util/aes_util_test.go
  5. 37 0
      util/encrypt_util.go
  6. 3 1
      web/basic_auth.go
  7. 29 0
      web/save.go
  8. 1 1
      web/writing.html

+ 1 - 0
entity/config.go

@@ -19,6 +19,7 @@ type Config struct {
 	BackupConfig []BackupConfig
 	Webhook
 	S3Config
+	EncryptKey string // 加密的key
 }
 
 // ConfigCache ConfigCache

+ 13 - 2
entity/s3_config.go

@@ -1,6 +1,7 @@
 package entity
 
 import (
+	"backup-x/util"
 	"errors"
 	"log"
 	"os"
@@ -31,8 +32,17 @@ func (s3Config S3Config) getSession() (*session.Session, error) {
 		return nil, errors.New("s3 config is empty")
 	}
 
-	creds := credentials.NewStaticCredentials(s3Config.AccessKey, s3Config.SecretKey, "")
-	_, err := creds.Get()
+	conf, err := GetConfigCache()
+	if err != nil {
+		return nil, err
+	}
+	secretKey, err := util.DecryptByEncryptKey(conf.EncryptKey, s3Config.SecretKey)
+	if err != nil {
+		return nil, err
+	}
+
+	creds := credentials.NewStaticCredentials(s3Config.AccessKey, secretKey, "")
+	_, err = creds.Get()
 	if err != nil {
 		log.Println(err)
 	}
@@ -77,6 +87,7 @@ func (s3Config S3Config) CreateBucketIfNotExist() {
 func (s3Config S3Config) UploadFile(fileName string) {
 	mySession, err := s3Config.getSession()
 	if err != nil {
+		log.Printf("创建对象存储会话失败, ERR: %s\n", err)
 		return
 	}
 

+ 82 - 0
util/aes_util.go

@@ -0,0 +1,82 @@
+package util
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+	"encoding/hex"
+	"fmt"
+	"io"
+)
+
+func GenerateKeyAndNonce() (string, string, error) {
+	// The key argument should be the AES key, either 16 or 32 bytes
+	// to select AES-128 or AES-256.
+	key := make([]byte, 32)
+	if _, err := io.ReadFull(rand.Reader, key); err != nil {
+		return "", "", err
+	}
+
+	// Never use more than 2^32 random nonces with a given key because of
+	// the risk of a repeat.
+	nonce := make([]byte, 12)
+	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
+		return "", "", err
+	}
+
+	return fmt.Sprintf("%x", key), fmt.Sprintf("%x", nonce), nil
+}
+
+func ValidateKeyAndNonce(keyHexStr, nonceHexStr string) ([]byte, []byte, error) {
+	key, err := hex.DecodeString(keyHexStr)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	nonce, err := hex.DecodeString(nonceHexStr)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return key, nonce, nil
+}
+
+func Encrypt(key []byte, nonce []byte, plainText string) (string, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return "", err
+	}
+
+	aesgcm, err := cipher.NewGCM(block)
+	if err != nil {
+		return "", err
+	}
+
+	cipherText := aesgcm.Seal(nil, nonce, []byte(plainText), nil)
+
+	return fmt.Sprintf("%x", cipherText), nil
+}
+
+func Decrypt(key []byte, nonce []byte, cipherHexStr string) (string, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return "", err
+	}
+
+	aesgcm, err := cipher.NewGCM(block)
+	if err != nil {
+		return "", err
+	}
+
+	cipherText, err := hex.DecodeString(cipherHexStr)
+	if err != nil {
+		return "", err
+	}
+
+	plainText, err := aesgcm.Open(nil, nonce, []byte(cipherText), nil)
+	if err != nil {
+		return "", err
+	}
+
+	return string(plainText), nil
+}

+ 34 - 0
util/aes_util_test.go

@@ -0,0 +1,34 @@
+package util
+
+import (
+	"testing"
+)
+
+func TestAesGcm(t *testing.T) {
+	keyHexStr, nonceHexStr, err := GenerateKeyAndNonce()
+	if err != nil {
+		t.Error(err)
+	}
+
+	key, nonce, err := ValidateKeyAndNonce(keyHexStr, nonceHexStr)
+	if err != nil {
+		t.Error(err)
+	}
+
+	plainText := "abc123"
+
+	cipherText, err := Encrypt(key, nonce, plainText)
+	if err != nil {
+		t.Error(err)
+	}
+
+	plainText2, err := Decrypt(key, nonce, cipherText)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if plainText2 != plainText {
+		t.Error("aes gcm error")
+	}
+
+}

+ 37 - 0
util/encrypt_util.go

@@ -0,0 +1,37 @@
+package util
+
+import "errors"
+
+// GenerateEncryptKey Generate a random EncryptKey
+func GenerateEncryptKey() (encryptKey string, err error) {
+	key, nonce, err := GenerateKeyAndNonce()
+	if err != nil {
+		return "", err
+	}
+
+	return key + nonce, nil
+}
+
+// EncryptByEncryptKey 加密
+func EncryptByEncryptKey(encryptKey string, orgStr string) (ecryptStr string, err error) {
+	if len(encryptKey) != 88 {
+		return "", errors.New("EncryptKey not corret")
+	}
+	key, nonce, err := ValidateKeyAndNonce(encryptKey[0:64], encryptKey[64:88])
+	if err != nil {
+		return "", err
+	}
+	return Encrypt(key, nonce, orgStr)
+}
+
+// DecryptByEncryptKey 解密
+func DecryptByEncryptKey(encryptKey string, encryptStr string) (decryptStr string, err error) {
+	if len(encryptKey) != 88 {
+		return "", errors.New("EncryptKey not corret")
+	}
+	key, nonce, err := ValidateKeyAndNonce(encryptKey[0:64], encryptKey[64:88])
+	if err != nil {
+		return "", err
+	}
+	return Decrypt(key, nonce, encryptStr)
+}

+ 3 - 1
web/basic_auth.go

@@ -2,6 +2,7 @@ package web
 
 import (
 	"backup-x/entity"
+	"backup-x/util"
 	"bytes"
 	"encoding/base64"
 	"log"
@@ -44,9 +45,10 @@ func BasicAuth(f ViewFunc) ViewFunc {
 			)
 			if err == nil {
 				pair := bytes.SplitN(payload, []byte(":"), 2)
+				pwd, _ := util.DecryptByEncryptKey(conf.EncryptKey, conf.Password)
 				if len(pair) == 2 &&
 					bytes.Equal(pair[0], []byte(conf.Username)) &&
-					bytes.Equal(pair[1], []byte(conf.Password)) {
+					bytes.Equal(pair[1], []byte(pwd)) {
 					ld.FailTimes = 0
 					// 执行被装饰的函数
 					f(w, r)

+ 29 - 0
web/save.go

@@ -3,6 +3,7 @@ package web
 import (
 	"backup-x/client"
 	"backup-x/entity"
+	"backup-x/util"
 	"net/http"
 	"strconv"
 	"strings"
@@ -10,8 +11,19 @@ import (
 
 // Save 保存
 func Save(writer http.ResponseWriter, request *http.Request) {
+	oldConf, _ := entity.GetConfigCache()
 	conf := &entity.Config{}
 
+	conf.EncryptKey = oldConf.EncryptKey
+	if conf.EncryptKey == "" {
+		encryptKey, err := util.GenerateEncryptKey()
+		if err != nil {
+			writer.Write([]byte("生成Key失败"))
+			return
+		}
+		conf.EncryptKey = encryptKey
+	}
+
 	// 覆盖以前的配置
 	conf.Username = strings.TrimSpace(request.FormValue("Username"))
 	conf.Password = request.FormValue("Password")
@@ -20,6 +32,14 @@ func Save(writer http.ResponseWriter, request *http.Request) {
 		writer.Write([]byte("请输入登录用户名/密码"))
 		return
 	}
+	if conf.Password != oldConf.Password {
+		encryptPasswd, err := util.EncryptByEncryptKey(conf.EncryptKey, conf.Password)
+		if err != nil {
+			writer.Write([]byte("加密失败"))
+			return
+		}
+		conf.Password = encryptPasswd
+	}
 
 	forms := request.PostForm
 	for index, projectName := range forms["ProjectName"] {
@@ -48,6 +68,15 @@ func Save(writer http.ResponseWriter, request *http.Request) {
 	conf.SecretKey = strings.TrimSpace(request.FormValue("SecretKey"))
 	conf.BucketName = strings.TrimSpace(request.FormValue("BucketName"))
 
+	if conf.SecretKey != oldConf.SecretKey {
+		secretKey, err := util.EncryptByEncryptKey(conf.EncryptKey, conf.SecretKey)
+		if err != nil {
+			writer.Write([]byte("加密失败"))
+			return
+		}
+		conf.SecretKey = secretKey
+	}
+
 	// 保存到用户目录
 	err := conf.SaveConfig()
 

+ 1 - 1
web/writing.html

@@ -210,7 +210,7 @@
               <div class="form-group row">
                 <label for="SecretKey" class="col-sm-2 col-form-label">SecretKey</label>
                 <div class="col-sm-10">
-                  <input class="form-control" name="SecretKey" id="SecretKey" value="{{.SecretKey}}" aria-describedby="SecretKey_help">
+                  <input class="form-control" type="password" name="SecretKey" id="SecretKey" value="{{.SecretKey}}" aria-describedby="SecretKey_help">
                 </div>
               </div>