feat: crypto

This commit is contained in:
Bobo
2025-05-07 13:52:07 +08:00
parent e90588b9ca
commit 363a18b1c8
13 changed files with 237 additions and 45 deletions

21
crypto/README.md Normal file
View File

@@ -0,0 +1,21 @@
# 加解密算法
## bcrypt
bcrypt是一个由美国计算机科学家尼尔斯·普罗沃斯Niels Provos以及大卫·马齐耶David Mazières根据Blowfish加密算法所设计的密码散列函数于1999年在USENIX中展示[1]。实现中bcrypt会使用一个加盐的流程以防御彩虹表攻击同时bcrypt还是适应性函数它可以借由增加迭代之次数来抵御日益增进的电脑运算能力透过暴力法破解。
由bcrypt加密的文件可在所有支持的操作系统和处理器上进行转移。它的口令必须是8至56个字符并将在内部被转化为448位的密钥。然而所提供的所有字符都具有十分重要的意义。密码越强大数据就越安全。
除了对数据进行加密默认情况下bcrypt在删除数据之前将使用随机数据三次覆盖原始输入文件以阻挠可能会获得计算机数据的人恢复数据的尝试。如果您不想使用此功能可设置禁用此功能。
具体来说bcrypt使用美国密码学家保罗·柯切尔的算法实现。随bcrypt一起发布的源代码对原始版本作了略微改动。
bcrypt哈希由多个部分组成。这些部分用于确定创建哈希的设置从而可以在不需要任何其他信息的情况下对其进行验证。
```text
$2a$10$ygWrRwHCzg2GUpz0UK40kuWAGva121VkScpcdMNsDCih2U/bL2qYy
```
- $2a$Prefix 表示使用bcrypt的算法版本。
- 10$Cost factor 表示加密的复杂度,值越大,计算时间越长。
- ygWrRwHCzg2GUpz0UK40kuWAGva121VkScpcdMNsDCih2U/bL2qYySalt 和 Hash前22个字符是盐值后面的字符是哈希值。

74
crypto/aes.go Normal file
View File

@@ -0,0 +1,74 @@
package crypto
import (
"bytes"
"fmt"
"crypto/aes"
"crypto/cipher"
"math/rand/v2"
)
// DefaultAESKey 默认AES密钥(16字节)
var DefaultAESKey = []byte("f51d66a73d8a0927")
// GenerateAESKey 生成AES密钥
func GenerateAESKey(length int) ([]byte, error) {
if length != 16 && length != 24 && length != 32 {
return nil, fmt.Errorf("invalid key length: %d, must be 16, 24, or 32 bytes", length)
}
key := make([]byte, length)
src := rand.ChaCha8{}
_, err := src.Read(key)
if err != nil {
return nil, err
}
return key, nil
}
// PKCS5Padding 填充明文
func PKCS5Padding(plaintext []byte, blockSize int) []byte {
padding := blockSize - len(plaintext)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(plaintext, padText...)
}
// PKCS5UnPadding 去除填充数据
func PKCS5UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
// AesEncrypt AES加密
func AesEncrypt(origData, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
//AES分组长度为128位所以blockSize=16单位字节
blockSize := block.BlockSize()
origData = PKCS5Padding(origData, blockSize)
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
crypted := make([]byte, len(origData))
blockMode.CryptBlocks(crypted, origData)
return crypted, nil
}
// AesDecrypt AES解密
func AesDecrypt(crypted, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
//AES分组长度为128位所以blockSize=16单位字节
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
origData := make([]byte, len(crypted))
blockMode.CryptBlocks(origData, crypted)
origData = PKCS5UnPadding(origData)
return origData, nil
}

50
crypto/aes_test.go Normal file
View File

@@ -0,0 +1,50 @@
package crypto
import (
"fmt"
"testing"
"encoding/base64"
"github.com/stretchr/testify/assert"
)
func TestDecryptAES(t *testing.T) {
//key的长度必须是16、24或者32字节分别用于选择AES-128, AES-192, or AES-256
aesKey, _ := GenerateAESKey(16)
aesKey = DefaultAESKey
plainText := []byte("cloud123456")
encryptText, err := AesEncrypt(plainText, aesKey)
if err != nil {
fmt.Println(err)
return
}
pass64 := base64.StdEncoding.EncodeToString(encryptText)
fmt.Printf("加密后:%v\n", pass64)
bytesPass, err := base64.StdEncoding.DecodeString(pass64)
if err != nil {
fmt.Println(err)
return
}
decryptText, err := AesDecrypt(bytesPass, aesKey)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("解密后:%s\n", decryptText)
assert.Equal(t, plainText, decryptText)
}
func TestGenerateAESKey_ValidLengths(t *testing.T) {
lengths := []int{16, 24, 32}
for _, length := range lengths {
key, err := GenerateAESKey(length)
assert.NoError(t, err)
assert.Equal(t, length, len(key))
t.Logf("%d : %x\n", length, string(key))
}
}

View File

@@ -1,8 +1,14 @@
package crypto
import (
"fmt"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"golang.org/x/crypto/bcrypt"
@@ -53,3 +59,44 @@ func GenerateSalt(length int) (string, error) {
}
return hex.EncodeToString(salt), nil
}
// DecryptAES AES解密函数
func DecryptAES(cipherText, key string) (string, error) {
// 将密文从Base64解码
cipherData, err := base64.StdEncoding.DecodeString(cipherText)
if err != nil {
return "", err
}
// 创建AES块
block, err := aes.NewCipher([]byte(key))
if err != nil {
return "", err
}
// 检查密文长度是否为块大小的倍数
if len(cipherData)%aes.BlockSize != 0 {
return "", fmt.Errorf("ciphertext is not a multiple of the block size")
}
// 创建CBC解密器
iv := cipherData[:aes.BlockSize] // 提取IV
cipherData = cipherData[aes.BlockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
// 解密
plainText := make([]byte, len(cipherData))
mode.CryptBlocks(plainText, cipherData)
// 去除填充
plainText = pkcs7Unpad(plainText)
return string(plainText), nil
}
// pkcs7Unpad PKCS7填充去除
func pkcs7Unpad(data []byte) []byte {
length := len(data)
padding := int(data[length-1])
return data[:length-padding]
}