From af710d0c9a884fa5561f868426a842a8f98c27c9 Mon Sep 17 00:00:00 2001 From: godo Date: Fri, 6 Dec 2024 18:24:25 +0800 Subject: [PATCH] change pwd --- godo/files/pwd.go | 297 ++++++++++++++++++++++++++++++++++ godo/files/pwdfile.go | 198 ----------------------- godo/files/pwdfile_test.go | 61 ------- godo/libs/config.go | 3 +- godo/libs/filecode.go | 319 +++++++++++++++++++++++++++++++++++++ godo/libs/filecode_test.go | 66 ++++++++ 6 files changed, 684 insertions(+), 260 deletions(-) create mode 100644 godo/files/pwd.go delete mode 100644 godo/files/pwdfile.go delete mode 100644 godo/files/pwdfile_test.go create mode 100644 godo/libs/filecode.go create mode 100644 godo/libs/filecode_test.go diff --git a/godo/files/pwd.go b/godo/files/pwd.go new file mode 100644 index 0000000..ec5d218 --- /dev/null +++ b/godo/files/pwd.go @@ -0,0 +1,297 @@ +package files + +import ( + "encoding/base64" + "fmt" + "godo/libs" + "io" + "net/http" + "os" + "path/filepath" + "strings" +) + +func HandleReadFile(w http.ResponseWriter, r *http.Request) { + path := r.URL.Query().Get("path") + if path == "" { + libs.ErrorMsg(w, "文件路径不能为空") + return + } + basePath, err := libs.GetOsDir() + if err != nil { + libs.ErrorMsg(w, err.Error()) + return + } + fullFilePath := filepath.Join(basePath, path) + file, err := os.Open(fullFilePath) + if err != nil { + libs.ErrorMsg(w, err.Error()) + return + } + fileData, err := io.ReadAll(file) + if err != nil { + libs.ErrorMsg(w, err.Error()) + return + } + text := string(fileData) + //log.Printf("isPwd: %v", strData) + if strings.HasPrefix(text, "link::") { + libs.SuccessMsg(w, text, "文件读取成功") + return + } + // 判断是否为加密文件 + isPwd := libs.IsEncryptedFile(text) + + if !isPwd { + // 未加密文件,直接返回 + text = base64.StdEncoding.EncodeToString(fileData) + // if len(text)%8 == 0 { + // text += " " + // } + //log.Printf("fileData: %s", content) + libs.SuccessMsg(w, text, "文件读取成功") + return + } + filePwd := r.Header.Get("pwd") + // 获取文件密钥 + fileSecret := libs.GetConfigString("filePwd") + if filePwd == "" && fileSecret == "" { + libs.Error(w, "加密文件,需要密码", "needPwd") + return + } + decodeFile := "" + needPwd := false + if fileSecret != "" { + decodeFile, err = libs.DecodeFile(fileSecret, text) + if err != nil { + libs.Error(w, "请输入文件密码", "needPwd") + return + } + needPwd = true + } + if filePwd != "" && !needPwd { + decodeFile, err = libs.DecodeFile(filePwd, text) + if err != nil { + libs.Error(w, "文件密码输入错误", "needPwd") + return + } + } + // if len(decodeFile)%8 == 0 { + // decodeFile = decodeFile + " " + // } + libs.SuccessMsg(w, decodeFile, "加密文件读取成功") +} +func HandleWriteFile(w http.ResponseWriter, r *http.Request) { + path := r.URL.Query().Get("path") + if err := validateFilePath(path); err != nil { + libs.HTTPError(w, http.StatusBadRequest, err.Error()) + return + } + basePath, err := libs.GetOsDir() + if err != nil { + libs.HTTPError(w, http.StatusInternalServerError, err.Error()) + return + } + fullFilePath := filepath.Join(basePath, path) + // 获取文件内容 + fileContent, _, err := r.FormFile("content") + if err != nil { + libs.HTTPError(w, http.StatusBadRequest, err.Error()) + return + } + defer fileContent.Close() + content, err := io.ReadAll(fileContent) + if err != nil { + libs.HTTPError(w, http.StatusInternalServerError, err.Error()) + return + } + // 打开文件,如果不存在则创建 + file, err := os.OpenFile(fullFilePath, os.O_RDWR|os.O_CREATE, 0644) + if err != nil { + libs.ErrorMsg(w, "Failed to create file.") + return + } + defer file.Close() + + oldContent, err := io.ReadAll(file) + text := string(oldContent) + //log.Printf("text: %s", text) + if err != nil { + libs.ErrorMsg(w, err.Error()) + return + } + isPwdFile := libs.IsEncryptedFile(text) + + filePwd := r.Header.Get("pwd") + // 获取文件密钥 + fileSecret := libs.GetConfigString("filePwd") + haslink := strings.HasPrefix(string(content), "link::") + //log.Printf("haslink:%v,fileSecret: %s,isPwdFile:%v,filePwd:%s", haslink, fileSecret, isPwdFile, filePwd) + needPwd := false + if !haslink { + needPwd = true + } + if fileSecret != "" || filePwd != "" { + needPwd = true + } + if isPwdFile { + needPwd = true + } + + // 即不是加密用户又不是加密文件 + if !needPwd { + // 直接写入新内容 + file.Truncate(0) + file.Seek(0, 0) + _, err = file.Write(content) + if err != nil { + libs.ErrorMsg(w, "Failed to write file content.") + return + } + CheckAddDesktop(path) + libs.SuccessMsg(w, "", "文件写入成功") + return + } + + //log.Printf("fileSecret:%s,filePwd:%s,ispwdfile:%v", fileSecret, filePwd, isPwdFile) + + // 是加密文件,写入需继续加密 + pwd := "" + if isPwdFile { + //先尝试系统解密 + if fileSecret != "" { + _, err := libs.DecodeFile(fileSecret, text) + if err == nil { + pwd = fileSecret + } + } + //先用户输入解密 + if pwd == "" && filePwd != "" { + _, err := libs.DecodeFile(filePwd, text) + if err == nil { + pwd = filePwd + } + } + + } else { + // 不是加密文件,先判断是否有用户输入 + if filePwd != "" { + pwd = filePwd + } else { + // 没有用户输入,则使用系统默认密码 + if fileSecret != "" { + pwd = fileSecret + } + } + } + if pwd == "" { + libs.Error(w, "文件密码错误", "needPwd") + //log.Printf("pwd is empty, returning error") + return + } + //log.Printf("pwd: %s", pwd) + + encryData, err := libs.EncodeFile(pwd, string(content)) + if err != nil { + libs.ErrorMsg(w, err.Error()) + return + } + + // 清空文件内容 + file.Truncate(0) + file.Seek(0, 0) + + _, err = file.Write([]byte(encryData)) + if err != nil { + libs.ErrorMsg(w, fmt.Sprintf("文件内容写入失败: %s", err.Error())) + return + } + CheckAddDesktop(path) + libs.SuccessMsg(w, "", "文件写入成功") + /* + //log.Printf("fileData: %s", string(fileData)) + configPwd, ishas := libs.GetConfig("filePwd") + // 如果不是加密文件或者exe文件 + if !ishas || strings.HasPrefix(string(fileData), "link::") { + // 没开启加密,直接明文写入 + _, err := newFile.Write(fileData) + if err != nil { + libs.HTTPError(w, http.StatusInternalServerError, "数据写入失败") + return + } + err = CheckAddDesktop(path) + if err != nil { + log.Printf("Error adding file to desktop: %s", err.Error()) + } + libs.SuccessMsg(w, "", "文件写入成功") + return + } else { + // 开启加密后,写入加密数据 + configPwdStr, ok := configPwd.(string) + if !ok { + libs.HTTPError(w, http.StatusInternalServerError, "配置文件密码格式错误") + return + } + // 拼接密码和加密后的数据 + passwordPrefix := fmt.Sprintf("@%s@", configPwdStr) + // _, err = newFile.WriteString(fmt.Sprintf("@%s@", configPwdStr)) + // if err != nil { + // libs.HTTPError(w, http.StatusInternalServerError, "密码写入失败") + // return + // } + entryData, err := libs.EncryptData(fileData, []byte(configPwdStr)) + if err != nil { + libs.HTTPError(w, http.StatusInternalServerError, "文件加密失败") + return + } + // 将密码前缀和加密数据拼接成一个完整的字节切片 + completeData := []byte(passwordPrefix + string(entryData)) + // 一次性写入文件 + _, err = newFile.Write(completeData) + if err != nil { + libs.HTTPError(w, http.StatusInternalServerError, "文件写入失败") + return + } + err = CheckAddDesktop(path) + if err != nil { + log.Printf("Error adding file to desktop: %s", err.Error()) + } + libs.SuccessMsg(w, "", "文件写入成功") + }*/ +} + +/* + func resEncode(w http.ResponseWriter, fileData []byte, filePwd string) { + decryptData, err := libs.DecryptData(fileData[34:], []byte(filePwd)) + if err != nil { + libs.ErrorMsg(w, err.Error()) + return + } + + //content := base64.StdEncoding.EncodeToString(decryptData) + if len(decryptData)%8 == 0 { + decryptData = append(decryptData, ' ') + } + // res := libs.APIResponse{Message: "加密文件读取成功", Data: decryptData} + // json.NewEncoder(w).Encode(res) + libs.SuccessMsg(w, decryptData, "加密文件读取成功") + } +*/ +func HandleSetFilePwd(w http.ResponseWriter, r *http.Request) { + Pwd := r.Header.Get("Pwd") + if Pwd == "" { + err := libs.DeleteConfig("filePwd") + if err != nil { + libs.ErrorMsg(w, fmt.Sprintf("取消加密失败:%s", err.Error())) + return + } + libs.SuccessMsg(w, nil, "取消加密成功") + return + } + err := libs.SetConfigByName("filePwd", Pwd) + if err != nil { + libs.ErrorMsg(w, err.Error()) + return + } + libs.SuccessMsg(w, nil, "设置密码成功") +} diff --git a/godo/files/pwdfile.go b/godo/files/pwdfile.go deleted file mode 100644 index b4b350d..0000000 --- a/godo/files/pwdfile.go +++ /dev/null @@ -1,198 +0,0 @@ -package files - -import ( - "fmt" - "godo/libs" - "io" - "log" - "net/http" - "os" - "path/filepath" - "strings" -) - -func HandleWriteFile(w http.ResponseWriter, r *http.Request) { - path := r.URL.Query().Get("path") - if err := validateFilePath(path); err != nil { - libs.HTTPError(w, http.StatusBadRequest, err.Error()) - return - } - basePath, err := libs.GetOsDir() - if err != nil { - libs.HTTPError(w, http.StatusInternalServerError, err.Error()) - return - } - fullFilePath := filepath.Join(basePath, path) - newFile, err := os.Create(fullFilePath) - if err != nil { - libs.HTTPError(w, http.StatusInternalServerError, err.Error()) - return - } - defer newFile.Close() - // 获取文件内容 - fileContent, _, err := r.FormFile("content") - if err != nil { - libs.HTTPError(w, http.StatusBadRequest, err.Error()) - return - } - defer fileContent.Close() - fileData, err := io.ReadAll(fileContent) - if err != nil { - libs.HTTPError(w, http.StatusInternalServerError, err.Error()) - return - } - //log.Printf("fileData: %s", string(fileData)) - configPwd, ishas := libs.GetConfig("filePwd") - // 如果不是加密文件或者exe文件 - if !ishas || strings.HasPrefix(string(fileData), "link::") { - // 没开启加密,直接明文写入 - _, err := newFile.Write(fileData) - if err != nil { - libs.HTTPError(w, http.StatusInternalServerError, "数据写入失败") - return - } - err = CheckAddDesktop(path) - if err != nil { - log.Printf("Error adding file to desktop: %s", err.Error()) - } - libs.SuccessMsg(w, "", "文件写入成功") - return - } else { - // 开启加密后,写入加密数据 - configPwdStr, ok := configPwd.(string) - if !ok { - libs.HTTPError(w, http.StatusInternalServerError, "配置文件密码格式错误") - return - } - // 拼接密码和加密后的数据 - passwordPrefix := fmt.Sprintf("@%s@", configPwdStr) - // _, err = newFile.WriteString(fmt.Sprintf("@%s@", configPwdStr)) - // if err != nil { - // libs.HTTPError(w, http.StatusInternalServerError, "密码写入失败") - // return - // } - entryData, err := libs.EncryptData(fileData, []byte(configPwdStr)) - if err != nil { - libs.HTTPError(w, http.StatusInternalServerError, "文件加密失败") - return - } - // 将密码前缀和加密数据拼接成一个完整的字节切片 - completeData := []byte(passwordPrefix + string(entryData)) - // 一次性写入文件 - _, err = newFile.Write(completeData) - if err != nil { - libs.HTTPError(w, http.StatusInternalServerError, "文件写入失败") - return - } - err = CheckAddDesktop(path) - if err != nil { - log.Printf("Error adding file to desktop: %s", err.Error()) - } - libs.SuccessMsg(w, "", "文件写入成功") - } -} - -func HandleReadFile(w http.ResponseWriter, r *http.Request) { - path := r.URL.Query().Get("path") - if path == "" { - libs.ErrorMsg(w, "文件路径不能为空") - return - } - basePath, err := libs.GetOsDir() - if err != nil { - libs.ErrorMsg(w, err.Error()) - return - } - fullFilePath := filepath.Join(basePath, path) - file, err := os.Open(fullFilePath) - if err != nil { - libs.ErrorMsg(w, err.Error()) - return - } - fileData, err := io.ReadAll(file) - if err != nil { - libs.ErrorMsg(w, err.Error()) - return - } - strData := string(fileData) - //log.Printf("isPwd: %v", strData) - if strings.HasPrefix(strData, "link::") { - libs.SuccessMsg(w, strData, "文件读取成功") - return - } - // 判断是否为加密文件 - isPwd := IsPwdFile(fileData) - - if !isPwd { - // 未加密文件,直接返回 - //content := base64.StdEncoding.EncodeToString(fileData) - if len(fileData)%8 == 0 { - fileData = append(fileData, ' ') - } - //log.Printf("fileData: %s", content) - libs.SuccessMsg(w, fileData, "文件读取成功") - return - } - Pwd := r.Header.Get("Pwd") - filePwd := strData[1:33] - // Pwd为空,info密码与文件密码做比对 - if Pwd == "" { - configPwd, ishas := libs.GetConfig("filePwd") - if !ishas { - libs.Error(w, "未设置密码", "needPwd") - return - } - configPwdStr, ok := configPwd.(string) - if !ok { - libs.Error(w, "后端配置文件密码格式错误", "needPwd") - return - } - // 校验密码 - if filePwd != configPwdStr { - libs.Error(w, "密码错误,请输入正确的密码", "needPwd") - return - } - resEncode(w, fileData, filePwd) - } else { - // Pwd不为空,Pwd与文件密码做比对 - if Pwd != filePwd { - libs.Error(w, "密码错误,请输入正确的密码", "needPwd") - return - } - resEncode(w, fileData, filePwd) - } -} -func resEncode(w http.ResponseWriter, fileData []byte, filePwd string) { - decryptData, err := libs.DecryptData(fileData[34:], []byte(filePwd)) - if err != nil { - libs.ErrorMsg(w, err.Error()) - return - } - - //content := base64.StdEncoding.EncodeToString(decryptData) - if len(decryptData)%8 == 0 { - decryptData = append(decryptData, ' ') - } - // res := libs.APIResponse{Message: "加密文件读取成功", Data: decryptData} - // json.NewEncoder(w).Encode(res) - libs.SuccessMsg(w, decryptData, "加密文件读取成功") -} - -func HandleSetFilePwd(w http.ResponseWriter, r *http.Request) { - Pwd := r.Header.Get("Pwd") - if Pwd == "" { - err := libs.DeleteConfig("filePwd") - if err != nil { - libs.ErrorMsg(w, fmt.Sprintf("取消加密失败:%s", err.Error())) - return - } - libs.SuccessMsg(w, nil, "取消加密成功") - return - } - err := libs.SetConfigByName("filePwd", Pwd) - if err != nil { - libs.ErrorMsg(w, err.Error()) - return - } - libs.SuccessMsg(w, nil, "设置密码成功") -} diff --git a/godo/files/pwdfile_test.go b/godo/files/pwdfile_test.go deleted file mode 100644 index 226e6e5..0000000 --- a/godo/files/pwdfile_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package files - -import ( - "fmt" - "godo/libs" - "io" - "os" - "testing" -) - -func Test_WriteFile(t *testing.T) { - filePath := "test.txt" - content := "hello world" - configPwd := "b854f92a0bb8462ad75239152081c12b" - file, err := os.Create(filePath) - if err != nil { - t.Error(err) - } - defer file.Close() - _, err = file.WriteString(fmt.Sprintf("@%s@", configPwd)) - if err != nil { - t.Error(err) - } - encryData, err := libs.EncryptData([]byte(content), []byte(configPwd)) - if err != nil { - t.Error(err) - } - _, err = file.Write(encryData) - if err != nil { - t.Error(err) - } -} - -func Test_ReadFile(t *testing.T) { - filePath := "test.txt" - pwd := "b854f92a0bb8462ad75239152081c12b" - file, err := os.Open(filePath) - if err != nil { - t.Error(err) - } - defer file.Close() - data, err := io.ReadAll(file) - if err != nil { - t.Error(err) - } - if pwd == "" { - fmt.Println(string(data)) - return - } - realPwd := data[1:33] - if pwd != string(realPwd) { - t.Error("密码错误") - return - } - content := data[34:] - decryData, err := libs.DecryptData(content, []byte(pwd)) - if err != nil { - t.Error(err) - } - fmt.Println(string(decryData)) -} diff --git a/godo/libs/config.go b/godo/libs/config.go index f1758d6..b8a45ea 100644 --- a/godo/libs/config.go +++ b/godo/libs/config.go @@ -23,6 +23,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "sync" ) @@ -138,7 +139,7 @@ func GetConfigString(name string) string { return "" } - return stringValue + return strings.Trim(stringValue, " ") } func ExistConfig(Name string) bool { _, exists := reqBodyMap.Load(Name) diff --git a/godo/libs/filecode.go b/godo/libs/filecode.go new file mode 100644 index 0000000..c98cbd2 --- /dev/null +++ b/godo/libs/filecode.go @@ -0,0 +1,319 @@ +package libs + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/md5" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/hex" + "fmt" + "io" + "log" + "strings" +) + +func GetEncryptedCode(data string) (string, error) { + // 检查是否以 @ 开头 + if !strings.HasPrefix(data, "@") { + return "", fmt.Errorf("invalid input format") + } + // 去掉开头的 @ + data = data[1:] + // 分割加密的私钥和加密的文本 + parts := strings.SplitN(data, "@", 2) + if len(parts) != 2 { + return "", fmt.Errorf("invalid input format") + } + return parts[0], nil +} +func IsEncryptedFile(data string) bool { + if len(data) < 2 { + return false + } + // 检查是否以 @ 开头 + if !strings.HasPrefix(data, "@") { + return false + } + // 去掉开头的 @ + data = data[1:] + // 分割加密的私钥和加密的文本 + parts := strings.SplitN(data, "@", 2) + if len(parts) != 2 { + return false + } + // 检查加密私钥和加密文本是否都不为空 + hexEncodedPrivateKey := parts[0] + // 检查十六进制字符串是否有效 + base64Str, err := hex.DecodeString(hexEncodedPrivateKey) + if err != nil { + return false + } + // 尝试将 Base64 字符串解码为字节切片 + _, err = base64.URLEncoding.DecodeString(string(base64Str)) + return err == nil +} + +// EncodeFile 加密文件 +func EncodeFile(password string, longText string) (string, error) { + privateKey, publicKey, err := GenerateRSAKeyPair(1024) + if err != nil { + fmt.Println("生成RSA密钥对失败:", err) + return "", err + } + // 使用公钥加密 + encryptedText := "" + if len(longText) > 0 { + encryptedText, err = EncryptLongText(longText, publicKey) + if err != nil { + return "", fmt.Errorf("加密失败:%v", err) + } + } + + pwd, err := hashAndMD5(password) + if err != nil { + return "", fmt.Errorf("加密密码失败:%v", err) + } + encryptedPrivateKey, err := EncryptPrivateKey(privateKey, pwd) + if err != nil { + fmt.Println("加密私钥失败:", err) + return "", fmt.Errorf("加密私钥失败") + } + + // 对 encryptedPrivateKey 进行 Base64 编码 + base64EncryptedPrivateKey := base64.URLEncoding.EncodeToString([]byte(encryptedPrivateKey)) + + // 将 Base64 编码后的字符串转换为十六进制字符串 + hexEncodedPrivateKey := hex.EncodeToString([]byte(base64EncryptedPrivateKey)) + + return "@" + hexEncodedPrivateKey + "@" + encryptedText, nil +} + +// DecodeFile 解密文件 +func DecodeFile(password string, encryptedData string) (string, error) { + // 去掉开头的@ + // log.Printf("encryptedData: %s", encryptedData) + if !strings.HasPrefix(encryptedData, "@") { + return "", fmt.Errorf("无效的加密数据格式") + } + encryptedData = encryptedData[1:] + + // 分割加密的私钥和加密的文本 + parts := strings.SplitN(encryptedData, "@", 2) + log.Printf("parts:%v", parts) + if len(parts) == 1 { + return "", nil + } + if len(parts) != 2 { + return "", fmt.Errorf("无效的加密数据格式") + } + + hexEncodedPrivateKey := parts[0] + encryptedText := parts[1] + if len(encryptedText) == 0 { + return "", nil + } + + // 将十六进制字符串转换回 Base64 编码字符串 + base64DecodedPrivateKey, err := hex.DecodeString(hexEncodedPrivateKey) + if err != nil { + return "", fmt.Errorf("十六进制字符串解码失败:%v", err) + } + + // 将 Base64 编码字符串解码回原始的 encryptedPrivateKey + encryptedPrivateKey, err := base64.URLEncoding.DecodeString(string(base64DecodedPrivateKey)) + if err != nil { + return "", fmt.Errorf("Base64字符串解码失败:%v", err) + } + + pwd, err := hashAndMD5(password) + if err != nil { + return "", fmt.Errorf("加密密码失败:%v", err) + } + + // 解密私钥 + privateKey, err := DecryptPrivateKey(string(encryptedPrivateKey), pwd) + if err != nil { + return "", fmt.Errorf("解密私钥失败:%v", err) + } + + // 解密文本 + decryptedText, err := DecryptLongText(encryptedText, privateKey) + if err != nil { + return "", fmt.Errorf("解密文本失败:%v", err) + } + + return decryptedText, nil +} + +// GenerateRSAKeyPair 生成RSA密钥对 +func GenerateRSAKeyPair(bits int) (*rsa.PrivateKey, *rsa.PublicKey, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + return nil, nil, err + } + + publicKey := &privateKey.PublicKey + + return privateKey, publicKey, nil +} +func GenertePrivateKey(password string, privateKey *rsa.PrivateKey) (*rsa.PrivateKey, error) { + encryptedPrivateKey, err := EncryptPrivateKey(privateKey, password) + if err != nil { + fmt.Println("加密私钥失败:", err) + return nil, fmt.Errorf("加密私钥失败") + } + decryptedPrivateKey, err := DecryptPrivateKey(encryptedPrivateKey, password) + if err != nil { + fmt.Println("解密私钥失败:", err) + return nil, fmt.Errorf("加密私钥失败") + } + return decryptedPrivateKey, nil +} + +// EncryptWithPublicKey 使用公钥加密数据 +func EncryptWithPublicKey(data []byte, publicKey *rsa.PublicKey) (string, error) { + ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, data, nil) + if err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(ciphertext), nil +} + +// DecryptWithPrivateKey 使用私钥解密数据 +func DecryptWithPrivateKey(encryptedData string, privateKey *rsa.PrivateKey) ([]byte, error) { + ciphertext, err := base64.URLEncoding.DecodeString(encryptedData) + if err != nil { + return nil, err + } + + plaintext, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, ciphertext, nil) + if err != nil { + return nil, err + } + return plaintext, nil +} + +// hashAndMD5 使用 MD5 哈希密码 +func hashAndMD5(password string) (string, error) { + hasher := md5.New() + _, err := hasher.Write([]byte(password)) + if err != nil { + return "", err + } + return hex.EncodeToString(hasher.Sum(nil)), nil +} + +// EncryptLongText 使用公钥加密长文本 +func EncryptLongText(longText string, publicKey *rsa.PublicKey) (string, error) { + // 分块加密 + blockSize := publicKey.N.BitLen()/8 - 2*sha256.Size - 2 + chunks := splitIntoChunks([]byte(longText), blockSize) + + var encryptedChunks []string + for _, chunk := range chunks { + encryptedChunk, err := EncryptWithPublicKey(chunk, publicKey) + if err != nil { + return "", err + } + encryptedChunks = append(encryptedChunks, encryptedChunk) + } + + return strings.Join(encryptedChunks, ":"), nil +} + +// DecryptLongText 使用私钥解密长文本 +func DecryptLongText(encryptedLongText string, privateKey *rsa.PrivateKey) (string, error) { + // 分块解密 + encryptedChunks := strings.Split(encryptedLongText, ":") + + var decryptedChunks [][]byte + for _, encryptedChunk := range encryptedChunks { + decryptedChunk, err := DecryptWithPrivateKey(encryptedChunk, privateKey) + if err != nil { + return "", err + } + decryptedChunks = append(decryptedChunks, decryptedChunk) + } + + return string(bytes.Join(decryptedChunks, nil)), nil +} + +// splitIntoChunks 将数据分割成指定大小的块 +func splitIntoChunks(data []byte, chunkSize int) [][]byte { + var chunks [][]byte + for i := 0; i < len(data); i += chunkSize { + end := i + chunkSize + if end > len(data) { + end = len(data) + } + chunks = append(chunks, data[i:end]) + } + return chunks +} + +// EncryptPrivateKey 使用AES加密RSA私钥 +func EncryptPrivateKey(privateKey *rsa.PrivateKey, password string) (string, error) { + privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey) + key := []byte(password) + + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return "", err + } + + ciphertext := gcm.Seal(nonce, nonce, privateKeyBytes, nil) + return base64.URLEncoding.EncodeToString(ciphertext), nil +} + +// DecryptPrivateKey 使用AES解密RSA私钥 +func DecryptPrivateKey(encryptedPrivateKey string, password string) (*rsa.PrivateKey, error) { + ciphertext, err := base64.URLEncoding.DecodeString(encryptedPrivateKey) + if err != nil { + return nil, err + } + + key := []byte(password) + + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonceSize := gcm.NonceSize() + if len(ciphertext) < nonceSize { + return nil, fmt.Errorf("ciphertext too short") + } + + nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] + privateKeyBytes, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, err + } + + privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBytes) + if err != nil { + return nil, err + } + + return privateKey, nil +} diff --git a/godo/libs/filecode_test.go b/godo/libs/filecode_test.go new file mode 100644 index 0000000..6565d2d --- /dev/null +++ b/godo/libs/filecode_test.go @@ -0,0 +1,66 @@ +package libs + +import ( + "strings" + "testing" +) + +func TestEncodeFile(t *testing.T) { + password := "testpassword" + longText := "This is a test message." + + encryptedData, err := EncodeFile(password, longText) + if err != nil { + t.Fatalf("EncodeFile failed: %v", err) + } + + if !strings.HasPrefix(encryptedData, "@") { + t.Errorf("Encoded data does not start with '@': %s", encryptedData) + } + + parts := strings.SplitN(encryptedData[1:], "@", 2) + if len(parts) != 2 { + t.Errorf("Encoded data format is incorrect: %s", encryptedData) + } + if !IsEncryptedFile(encryptedData) { + t.Errorf("IsEncryptedFile returned false for valid encrypted data: %s", encryptedData) + } +} + +func TestDecodeFile(t *testing.T) { + password := "96e79218965eb72c92a549dd5a330112" + longText := "This is a test message." + + encryptedData, err := EncodeFile(password, longText) + if err != nil { + t.Fatalf("EncodeFile failed: %v", err) + } + + decryptedText, err := DecodeFile(password, encryptedData) + if err != nil { + t.Fatalf("DecodeFile failed: %v", err) + } + + if decryptedText != longText { + t.Errorf("Decrypted text does not match original: expected '%s', got '%s'", longText, decryptedText) + } +} + +func TestIsEncryptedFile(t *testing.T) { + password := "testpassword" + longText := "This is a test message." + + encryptedData, err := EncodeFile(password, longText) + if err != nil { + t.Fatalf("EncodeFile failed: %v", err) + } + + if !IsEncryptedFile(encryptedData) { + t.Errorf("IsEncryptedFile returned false for valid encrypted data: %s", encryptedData) + } + + invalidData := "Invalid@Data" + if IsEncryptedFile(invalidData) { + t.Errorf("IsEncryptedFile returned true for invalid encrypted data: %s", invalidData) + } +}