xy_sh-20260724171749/xy_sh/valid.md

177 lines
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// File: xy_sh/pkg/crypto/crypto.go
```go
package crypto
import (
"bytes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
"math/big"
"time"
"github.com/tjfoc/gmsm/sm3"
"github.com/tjfoc/gmsm/sm4"
)
// ==================== SM4-CBC 加密/解密 ====================
// SM4CBCEncrypt SM4-CBC模式加密
func SM4CBCEncrypt(plaintext []byte, key []byte) (string, error) {
if len(key) != 16 {
return "", fmt.Errorf("SM4密钥长度必须为16字节")
}
block, err := sm4.NewCipher(key)
if err != nil {
return "", fmt.Errorf("创建SM4 cipher失败: %v", err)
}
padded := pkcs7Padding(plaintext, block.BlockSize())
iv := make([]byte, block.BlockSize())
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return "", fmt.Errorf("生成IV失败: %v", err)
}
mode := cipher.NewCBCEncrypter(block, iv)
ciphertext := make([]byte, len(padded))
mode.CryptBlocks(ciphertext, padded)
result := append(iv, ciphertext...)
return base64.StdEncoding.EncodeToString(result), nil
}
// SM4CBCDecrypt SM4-CBC模式解密
func SM4CBCDecrypt(encryptedData string, key []byte) ([]byte, error) {
if len(key) != 16 {
return nil, fmt.Errorf("SM4密钥长度必须为16字节")
}
data, err := base64.StdEncoding.DecodeString(encryptedData)
if err != nil {
return nil, fmt.Errorf("base64解码失败: %v", err)
}
block, err := sm4.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("创建SM4 cipher失败: %v", err)
}
blockSize := block.BlockSize()
if len(data) < blockSize {
return nil, fmt.Errorf("数据长度不足")
}
iv := data[:blockSize]
ciphertext := data[blockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
plaintext := make([]byte, len(ciphertext))
mode.CryptBlocks(plaintext, ciphertext)
plaintext, err = pkcs7UnPadding(plaintext)
if err != nil {
return nil, fmt.Errorf("去除填充失败: %v", err)
}
return plaintext, nil
}
// pkcs7Padding PKCS7填充
func pkcs7Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
// pkcs7UnPadding 去除PKCS7填充
func pkcs7UnPadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, fmt.Errorf("数据为空")
}
padding := int(data[length-1])
if padding > length || padding == 0 {
return nil, fmt.Errorf("无效的填充")
}
for i := length - padding; i < length; i++ {
if data[i] != byte(padding) {
return nil, fmt.Errorf("无效的填充")
}
}
return data[:length-padding], nil
}
// ==================== SM3 哈希 ====================
// SM3Hash 计算SM3哈希值
func SM3Hash(data []byte, encoding string) (string, error) {
h := sm3.New()
h.Write(data)
hashBytes := h.Sum(nil)
if encoding == "base64" {
return base64.StdEncoding.EncodeToString(hashBytes), nil
}
return hex.EncodeToString(hashBytes), nil
}
// SM3HashString 计算字符串的SM3哈希值
func SM3HashString(data string, encoding string) (string, error) {
return SM3Hash([]byte(data), encoding)
}
// SM3Verify 验证数据与哈希值是否匹配
func SM3Verify(data []byte, hash string, encoding string) (bool, error) {
expected, err := SM3Hash(data, encoding)
if err != nil {
return false, err
}
return expected == hash, nil
}
// HMACSM3 HMAC-SM3计算
func HMACSM3(data []byte, key []byte) string {
h := hmac.New(sm3.New, key)
h.Write(data)
return hex.EncodeToString(h.Sum(nil))
}
// SM3WithSalt SM3加盐哈希对应Java的SmUtil.sm3WithSalt
// 实现方式使用HMAC-SM3将salt作为HMAC密钥
func SM3WithSalt(data []byte, salt []byte) (string, error) {
h := hmac.New(sm3.New, salt)
h.Write(data)
return hex.EncodeToString(h.Sum(nil)), nil
}
// ==================== 时间戳和随机数 ====================
// GenerateTimestamp 生成秒级时间戳
func GenerateTimestamp() string {
return fmt.Sprintf("%d", time.Now().Unix())
}
// GenerateTimestampMillis 生成毫秒级时间戳
func GenerateTimestampMillis() string {
return fmt.Sprintf("%d", time.Now().UnixMilli())
}
// GenerateNonce 生成指定长度的随机字符串
func GenerateNonce(length int) (string, error) {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, length)
for i := range b {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
if err != nil {
return "", err
}
b[i] = charset[num.Int64()]
}
return string(b), nil
}
```