添加文件: crypto.go

This commit is contained in:
renzhiyuan 2026-07-21 18:16:24 +08:00
parent 4395946ad3
commit 9c6cf762cc
1 changed files with 142 additions and 0 deletions

142
crypto.go Normal file
View File

@ -0,0 +1,142 @@
package xyshanghai
import (
"crypto/cipher"
"encoding/base64"
"encoding/hex"
"fmt"
"github.com/tjfoc/gmsm/sm3"
"github.com/tjfoc/gmsm/sm4"
)
// SM4Encrypt 使用SM4 ECB模式加密数据返回base64编码字符串
func SM4Encrypt(key, plaintext []byte) (string, error) {
block, err := sm4.NewCipher(key)
if err != nil {
return "", fmt.Errorf("sm4 new cipher: %w", err)
}
// PKCS7填充
plaintext = pkcs7Padding(plaintext, block.BlockSize())
// ECB模式
mode := newECBEncrypter(block)
ciphertext := make([]byte, len(plaintext))
mode.CryptBlocks(ciphertext, plaintext)
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// SM4Decrypt 解密base64编码的SM4 ECB密文返回明文
func SM4Decrypt(key []byte, ciphertextBase64 string) ([]byte, error) {
ciphertext, err := base64.StdEncoding.DecodeString(ciphertextBase64)
if err != nil {
return nil, fmt.Errorf("base64 decode: %w", err)
}
block, err := sm4.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("sm4 new cipher: %w", err)
}
mode := newECBDecrypter(block)
plaintext := make([]byte, len(ciphertext))
mode.CryptBlocks(plaintext, ciphertext)
// 去除PKCS7填充
plaintext, err = pkcs7Unpadding(plaintext, block.BlockSize())
if err != nil {
return nil, fmt.Errorf("pkcs7 unpadding: %w", err)
}
return plaintext, nil
}
// SM3Sign 使用SM3加盐签名返回十六进制字符串
func SM3Sign(salt []byte, data string) string {
h := sm3.Sm3WithSalt(salt)
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
// pkcs7Padding PKCS7填充
func pkcs7Padding(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
padtext := make([]byte, padding)
for i := range padtext {
padtext[i] = byte(padding)
}
return append(src, padtext...)
}
// pkcs7Unpadding 去除PKCS7填充
func pkcs7Unpadding(src []byte, blockSize int) ([]byte, error) {
length := len(src)
if length == 0 {
return nil, fmt.Errorf("empty data")
}
unpadding := int(src[length-1])
if unpadding > blockSize || unpadding == 0 {
return nil, fmt.Errorf("invalid padding")
}
for i := length - unpadding; i < length; i++ {
if src[i] != byte(unpadding) {
return nil, fmt.Errorf("invalid padding")
}
}
return src[:length-unpadding], nil
}
// ecb 实现ECB模式标准库未提供
type ecb struct {
b cipher.Block
blockSize int
}
func newECB(b cipher.Block) *ecb {
return &ecb{
b: b,
blockSize: b.BlockSize(),
}
}
type ecbEncrypter ecb
func newECBEncrypter(b cipher.Block) cipher.BlockMode {
return (*ecbEncrypter)(newECB(b))
}
func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 {
panic("crypto/cipher: input not full blocks")
}
if len(dst) < len(src) {
panic("crypto/cipher: output smaller than input")
}
for i := 0; i < len(src); i += x.blockSize {
x.b.Encrypt(dst[i:i+x.blockSize], src[i:i+x.blockSize])
}
}
type ecbDecrypter ecb
func newECBDecrypter(b cipher.Block) cipher.BlockMode {
return (*ecbDecrypter)(newECB(b))
}
func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 {
panic("crypto/cipher: input not full blocks")
}
if len(dst) < len(src) {
panic("crypto/cipher: output smaller than input")
}
for i := 0; i < len(src); i += x.blockSize {
x.b.Decrypt(dst[i:i+x.blockSize], src[i:i+x.blockSize])
}
}