package main
import (
"bufio"
"crypto/sha256"
"database/sql"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"sync"
"time"
_ "github.com/mattn/go-sqlite3"
)
// 加密字符串
func GetNegation(strb []byte) []byte {
var strsN []byte
for i, b := range strb {
ii := i ^ (i % 3)
strsN = append(strsN, ^b-uint8(ii))
}
return strsN
}
//md5值,参数为字符串,输出md5字符串
func GetSha256(str string) string {
strM := sha256.Sum256([]byte(str))
return hex.EncodeToString(strM[:])
}
// 保存修改后的文件,文件名为md5值,内容为1024字节后内容
func SaveFile(fileMd5 string, content []byte) bool {
contentNeg := GetNegation(content)
err := ioutil.WriteFile(fileMd5, contentNeg, 0777) //将源文件,写入目标文件
if err != nil {
return false
}
return true
}
//返回文件名列表
func ReadFolder(fileDir string, extlist []string) []string {
var fileNameList []string
files, _ := ioutil.ReadDir(fileDir) //读取目录
for _, onefile := range files { //遍历目录下文件
if !onefile.IsDir() { //是文件
fileName := onefile.Name()
fileNames := strings.Split(fileName, ".")
ext := fileNames[len(fileNames)-1]
for _, e := range extlist {
if strings.EqualFold(ext, e) {
fileNameList = append(fileNameList, fileName)
break
}
}
}
}
return fileNameList
}
//返回加密后的文件列表
func ReadFolderNeg(fileDir string) []string {
var fileNameList []string
files, _ := ioutil.ReadDir(fileDir) //读取目录
for _, onefile := range files { //遍历目录下文件
if !onefile.IsDir() { //是文件
fileName := onefile.Name()
if len(fileName) == 64 {
fileNameList = append(fileNameList, fileName)
}
}
}
return fileNameList
}
//删除文件
func DelFile(file *os.File, path string, jiajie string) {
if file != nil {
file.Close()
}
if IsFileExist(path) {
err := os.Remove(path)
if err == nil {
// fmt.Printf("%s -> %s删除成功!\n", path, jiajie)
return
}
}
// fmt.Printf("%s -> %s删除失败!\n", path, jiajie)
}
//判断文件是否存在
func IsFileExist(path string) bool {
fileInfo, err := os.Stat(path)
if err != nil {
return false
}
if fileInfo.IsDir() { //是目录
return false
}
return true
}
//错误处理
func CheckErr(err error) {
if err != nil {
panic(err)
}
}
//保存到数据库
func SaveFileToDb(db *sql.DB, fileName256 string, fileName string, content string, password string) bool {
stmt, err := db.Prepare("insert into secret_key(fileName256,fileName,content,password) values(?,?,?,?)")
defer func() {
if stmt != nil {
stmt.Close()
}
}()
CheckErr(err)
rwm.Lock()
res, err := stmt.Exec(fileName256, fileName, content, password)
rwm.Unlock()
if stmt != nil {
stmt.Close()
}
CheckErr(err)
id, err := res.LastInsertId()
CheckErr(err)
if id > 0 {
return true
}
return false
}
//数据库搜索
func selectDb(db *sql.DB, sql string) (string, string, string, string) {
rows, err := db.Query(sql)
defer func() {
if rows != nil {
rows.Close()
}
}()
CheckErr(err)
var (
uid int
fileNameb256 string
fileNameb string
contentb string
password string
)
rows.Next()
rows.Scan(&uid, &fileNameb256, &fileNameb, &contentb, &password)
return fileNameb256, fileNameb, contentb, password
}
//删除数据项
func DelData(db *sql.DB, fileName256 string) {
stmt3, err := db.Prepare("delete from secret_key where fileName256=?")
defer func() {
if stmt3 != nil {
stmt3.Close()
}
}()
CheckErr(err)
rwm.Lock() //加锁
_, err = stmt3.Exec(fileName256)
rwm.Unlock() //解锁
CheckErr(err)
}
//加密过程
func jiami(fileName string, db *sql.DB, jiamiPw string, jiajie string) {
// delfileName := fileName
waiB := 0
fileSha256 := GetSha256(fileName) //文件名改为哈希256
sql1 := fmt.Sprintf(`select * from secret_key where fileName256="%s"`, fileSha256)
sfileSha256, _, sbeforeNegation, _ := selectDb(db, sql1) //查找数据库文件名是否已存在
srcFile, err := os.Open(fileName) //读取原文件
if err != nil { //读取错误
// fmt.Println(fileName, " -> 打开错误!")
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
}
dstFile, err2 := os.OpenFile(fileSha256, os.O_WRONLY|os.O_CREATE, 0666) //加密后的文件
if err2 != nil { //读取错误
// fmt.Println(fileSha256, " -> 打开错误!")
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
}
defer func() { //关闭打开的文件
srcFile.Close()
dstFile.Close()
}()
r := bufio.NewReader(srcFile) //读文件
w := bufio.NewWriter(dstFile) //写文件
buf := make([]byte, 4096) //读写缓冲区
var sBuf []byte //加密后的数据
bbb := 0 //计数器,第一次读到的文件内容存储到数据库,后面内容存储到文件中
for {
n, err3 := r.Read(buf) //读文件到缓冲区
if err3 != nil { //读文件错误
if err3 == io.EOF { //读取完毕
w.Flush()
fmt.Println(fileName, " -> 完成!")
DelFile(srcFile, fileName, jiajie) //删除原文件
} else { //非读取完毕错误
// fmt.Println(fileName, " -> 读取错误!")
}
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
} else { //读取正常
if bbb == 0 {
//加密保存数据库
beforeNegation := string(GetNegation(buf[:n])) // 加密数据库文件内容
if sfileSha256 != "" {
if sbeforeNegation == beforeNegation { //数据库有相同内容
if IsFileExist(sfileSha256) { //加密后文件存在
fmt.Println(fileName, ":文件已加密!")
DelFile(srcFile, fileName, jiajie) //直接删除原文件
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
}
} else { //数据库没有相同内容,只是文件名相同
ns := time.Now().UnixNano()
fileName = fmt.Sprintf("%v%s", ns, fileName) // 重命名文件名
fileSha256 = GetSha256(fileName) //文件名哈希256
}
}
fileNegation := string(GetNegation([]byte(fileName))) //原文件名加密存储到数据库
password := GetSha256(jiamiPw) //密码的哈希值
if SaveFileToDb(db, fileSha256, fileNegation, beforeNegation, password) { //保存内容到数据库
// fmt.Println(fileName, " -> 数据库保存成功!")
}
} else {
//加密保存文件
sBuf = GetNegation(buf[:n]) //加密数据
_, err4 := w.Write(sBuf) //保存数据
if err4 != nil { //保存错误
fmt.Println(fileSha256, " -> 保存错误!")
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
}
}
}
bbb++ //计数器加一
}
}
// 解密过程
func jiemi(fileName string, db *sql.DB, jiemiPw string, jiajie string) {
sql := fmt.Sprintf(`select * from secret_key where fileName256="%s"`, fileName)
fileNameDb256, fileNameDb, contentDb, passwordDb := selectDb(db, sql) //查找数据库
waiB := 0
if fileNameDb256 != "" && GetSha256(jiemiPw) == passwordDb { //数据存在加密文件,并验证密码
fileNameDbs := string(GetNegation([]byte(fileNameDb))) //解密后文件名
contentDbs := GetNegation([]byte(contentDb)) //解密后前半内容
//文件名已存在,判断前半内容是否相同,若相同,直接删除加密文件、数据库内容
if IsFileExist(fileNameDbs) { //解密后文件名已存在
srcFile, err := os.Open(fileNameDbs) //读取原文件
if err != nil { //读取错误
// fmt.Println(fileName, " -> 打开错误!")
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
}
defer func() { //关闭打开的文件
srcFile.Close()
}()
r := bufio.NewReader(srcFile) //读文件
buf := make([]byte, 4096) //读写缓冲区
n, err3 := r.Read(buf) //读文件到缓冲区
if err3 != nil { //读文件错误
// fmt.Println(fileName, " -> 读取错误!")
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
} else { //读取正常
if string(buf[:n]) == string(contentDbs) {
//解密后文件已存在
DelData(db, fileNameDb256) //解密文件已存在,删除数据库中内容
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
} else {
//解密后文件名存在,但是文件内容不同
ns := time.Now().UnixNano()
fileNameDbs = fmt.Sprintf("%v%s", ns, fileName) // 重命名文件名
}
}
}
f, err2 := os.Open(fileName)
if err2 != nil {
// fmt.Println(fileName, " -> 打开错误!")
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
}
f2, err3 := os.OpenFile(fileNameDbs, os.O_WRONLY|os.O_CREATE, 0666)
if err3 != nil {
// fmt.Println(fileNameDbs, " -> 打开错误!")
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
}
defer func() {
f.Close()
f2.Close()
}()
r := bufio.NewReader(f)
w := bufio.NewWriter(f2)
buf2 := make([]byte, 4096)
ddd := 0
var sBuf []byte
for {
if ddd == 0 {
w.Write(contentDbs)
} else {
n, err4 := r.Read(buf2)
if err4 != nil {
if err4 == io.EOF {
w.Flush()
fmt.Println(fileName, " -> 完成!")
DelFile(f, fileNameDb256, jiajie) //解密成功,删除加密后的文件
DelData(db, fileNameDb256) //解密成功,删除数据库中内容
} else { //非读取完毕错误
// fmt.Println(fileName, " -> 读取错误!")
}
if waiB == 0 {
waitgroup.Done()
waiB++
}
return
} else {
sBuf = GetNegation(buf2[:n]) //解密数据
w.Write(sBuf)
}
}
ddd++
}
} else {
fmt.Println(fileName, " -> 密码错误!")
}
if waiB == 0 {
waitgroup.Done()
waiB++
}
}
var waitgroup sync.WaitGroup
var rwm sync.RWMutex
//主函数
func main() {
db, err := sql.Open("sqlite3", "./重要勿删")
defer db.Close()
if err != nil {
// fmt.Println("打开数据库错误!")
}
sqlTable := `
CREATE TABLE IF NOT EXISTS secret_key(
uid INTEGER PRIMARY KEY AUTOINCREMENT,
fileName256 VARCHAR NULL,
fileName VARCHAR NULL,
content VARCHAR NULL,
password VARCHAR NULL
);
`
db.Exec(sqlTable)
fileDir := "." //需要加密的相对路径
var selectMenus string //选择菜单
var extlist []string //加密扩展名列表
var ext string //输入的加密扩展名
var jiemiPw string
var jiamiPw string
for {
selectMenus = ""
fmt.Println("\n\n************************* 加密、解密小程序 *************************")
fmt.Print("1. 加密 2. 解密 3. 退出\n请输入数字并回车:")
fmt.Scanln(&selectMenus) //主菜单
if selectMenus == "1" { //选择加密
extlist = []string{} //加密扩展名清空
fmt.Print("请添加一个需要加密的扩展名(如:jpg)输入后回车:")
for { //循环输入多个扩展名
fmt.Scanln(&ext) //输入需要加密的扩展名
if ext == "11" || ext == "33" { //当等于11,33时退出输入扩展名
break
}
if ext == "22" { //22清空输入的扩展名
extlist = []string{}
fmt.Print("请重新输入需要加密的扩展名(如:jpg):")
continue
}
extlist = append(extlist, ext) //扩展名添加到列表
fmt.Print("您输入的扩展名为:")
for _, i := range extlist { //循环输出输入的扩展名
fmt.Print(i, " ")
}
fmt.Print("(11.开始加密 22.重新输入 33.退出加密):")
}
// extlist = []string{"jpg", "mp4"}
if ext == "33" { //33时返回主菜单
continue
}
fmt.Print("请输入加密密码:")
fmt.Scanln(&jiamiPw)
for _, fileName := range ReadFolder(fileDir, extlist) {
waitgroup.Add(1)
go jiami(fileName, db, jiamiPw, "加密")
}
waitgroup.Wait()
} else if selectMenus == "2" {
fmt.Print("请输入解密密码:")
fmt.Scan(&jiemiPw)
for _, fileName := range ReadFolderNeg(fileDir) { //读取已加密的文件列表
waitgroup.Add(1)
go jiemi(fileName, db, jiemiPw, "解密") //解密
}
waitgroup.Wait()
} else if selectMenus == "3" {
break
} else {
fmt.Println("输入错误,请重新选择!")
}
}
}