xy-shanghai-20260721-181118/crypto.go

142 lines
3.4 KiB
Go
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.

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])
}
}