diff --git a/ymt_v3_generate/res.md b/ymt_v3_generate/res.md new file mode 100644 index 0000000..8415233 --- /dev/null +++ b/ymt_v3_generate/res.md @@ -0,0 +1,271 @@ +```go +package ymt_v3_generate + +import ( + "bytes" + "crypto" + "crypto/aes" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "reflect" + "sort" + "strings" +) + +// EncryptType 加密类型 +type EncryptType string + +const ( + EncryptTypeAES EncryptType = "aes" // AES ECB模式 + EncryptTypeSM4 EncryptType = "sm4" // SM4 CBC模式(暂未实现,仅占位) +) + +// Crypto 加密工具 +type Crypto struct { + key []byte + encryptType EncryptType +} + +// NewCrypto 创建加密工具 +func NewCrypto(key string, encryptType EncryptType) *Crypto { + return &Crypto{ + key: []byte(key), + encryptType: encryptType, + } +} + +// Encrypt 加密明文,返回base64编码的密文 +func (c *Crypto) Encrypt(plaintext []byte) (string, error) { + switch c.encryptType { + case EncryptTypeAES: + return c.aesECBEncrypt(plaintext) + case EncryptTypeSM4: + return "", errors.New("SM4 encryption not implemented") + default: + return "", fmt.Errorf("unsupported encrypt type: %s", c.encryptType) + } +} + +// Decrypt 解密密文(base64编码),返回明文 +func (c *Crypto) Decrypt(ciphertext string) ([]byte, error) { + cipherData, err := base64.StdEncoding.DecodeString(ciphertext) + if err != nil { + return nil, fmt.Errorf("base64 decode failed: %w", err) + } + switch c.encryptType { + case EncryptTypeAES: + return c.aesECBDecrypt(cipherData) + case EncryptTypeSM4: + return nil, errors.New("SM4 decryption not implemented") + default: + return nil, fmt.Errorf("unsupported encrypt type: %s", c.encryptType) + } +} + +// aesECBEncrypt AES ECB模式加密 +func (c *Crypto) aesECBEncrypt(plaintext []byte) (string, error) { + block, err := aes.NewCipher(c.key) + if err != nil { + return "", err + } + // PKCS7填充 + plaintext = pkcs7Pad(plaintext, block.BlockSize()) + ciphertext := make([]byte, len(plaintext)) + // ECB模式:直接分组加密 + for start := 0; start < len(plaintext); start += block.BlockSize() { + block.Encrypt(ciphertext[start:start+block.BlockSize()], plaintext[start:start+block.BlockSize()]) + } + return base64.StdEncoding.EncodeToString(ciphertext), nil +} + +// aesECBDecrypt AES ECB模式解密 +func (c *Crypto) aesECBDecrypt(ciphertext []byte) ([]byte, error) { + block, err := aes.NewCipher(c.key) + if err != nil { + return nil, err + } + if len(ciphertext)%block.BlockSize() != 0 { + return nil, errors.New("ciphertext is not a multiple of block size") + } + plaintext := make([]byte, len(ciphertext)) + for start := 0; start < len(ciphertext); start += block.BlockSize() { + block.Decrypt(plaintext[start:start+block.BlockSize()], ciphertext[start:start+block.BlockSize()]) + } + // 去除PKCS7填充 + plaintext, err = pkcs7Unpad(plaintext, block.BlockSize()) + if err != nil { + return nil, err + } + return plaintext, nil +} + +// pkcs7Pad PKCS7填充 +func pkcs7Pad(data []byte, blockSize int) []byte { + padding := blockSize - len(data)%blockSize + padText := bytes.Repeat([]byte{byte(padding)}, padding) + return append(data, padText...) +} + +// pkcs7Unpad 去除PKCS7填充 +func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) { + if len(data) == 0 { + return nil, errors.New("data is empty") + } + padding := int(data[len(data)-1]) + if padding > blockSize || padding == 0 { + return nil, errors.New("invalid padding") + } + for i := len(data) - padding; i < len(data); i++ { + if int(data[i]) != padding { + return nil, errors.New("invalid padding") + } + } + return data[:len(data)-padding], nil +} + +// Signer 签名工具 +type Signer struct { + privateKey *rsa.PrivateKey +} + +// NewSigner 创建签名工具 +func NewSigner(privateKeyPEM string) (*Signer, error) { + block, _ := pem.Decode([]byte(privateKeyPEM)) + if block == nil { + return nil, errors.New("failed to decode private key PEM") + } + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + // 尝试PKCS1 + key, err = x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("failed to parse private key: %w", err) + } + } + rsaKey, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("private key is not RSA") + } + return &Signer{privateKey: rsaKey}, nil +} + +// Sign 对数据进行签名,返回base64编码的签名 +func (s *Signer) Sign(data string) (string, error) { + hash := sha256.Sum256([]byte(data)) + signature, err := rsa.SignPKCS1v15(rand.Reader, s.privateKey, crypto.SHA256, hash[:]) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(signature), nil +} + +// Verifier 验签工具 +type Verifier struct { + publicKey *rsa.PublicKey +} + +// NewVerifier 创建验签工具 +func NewVerifier(publicKeyPEM string) (*Verifier, error) { + block, _ := pem.Decode([]byte(publicKeyPEM)) + if block == nil { + return nil, errors.New("failed to decode public key PEM") + } + key, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("failed to parse public key: %w", err) + } + rsaKey, ok := key.(*rsa.PublicKey) + if !ok { + return nil, errors.New("public key is not RSA") + } + return &Verifier{publicKey: rsaKey}, nil +} + +// Verify 验证签名 +func (v *Verifier) Verify(data, signatureBase64 string) error { + sig, err := base64.StdEncoding.DecodeString(signatureBase64) + if err != nil { + return fmt.Errorf("base64 decode signature failed: %w", err) + } + hash := sha256.Sum256([]byte(data)) + return rsa.VerifyPKCS1v15(v.publicKey, crypto.SHA256, hash[:], sig) +} + +// BuildPlaintext 构建待加密/签名的明文:将结构体转为JSON,去掉零值字段,按key排序 +func BuildPlaintext(v interface{}) (string, error) { + val := reflect.ValueOf(v) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if val.Kind() != reflect.Struct { + return "", errors.New("input must be a struct or pointer to struct") + } + // 转换为map[string]interface{},过滤零值 + m := make(map[string]interface{}) + typ := val.Type() + for i := 0; i < val.NumField(); i++ { + field := typ.Field(i) + jsonTag := field.Tag.Get("json") + if jsonTag == "" || jsonTag == "-" { + continue + } + // 处理json tag,可能包含omitempty + name := strings.Split(jsonTag, ",")[0] + fieldVal := val.Field(i) + if isZeroValue(fieldVal) { + continue + } + m[name] = fieldVal.Interface() + } + // 按key排序 + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + // 构建有序JSON + var buf bytes.Buffer + buf.WriteByte('{') + for idx, k := range keys { + if idx > 0 { + buf.WriteByte(',') + } + buf.WriteString(fmt.Sprintf(`"%s":`, k)) + // 将值转为JSON + valBytes, err := json.Marshal(m[k]) + if err != nil { + return "", err + } + buf.Write(valBytes) + } + buf.WriteByte('}') + return buf.String(), nil +} + +// isZeroValue 判断反射值是否为零值 +func isZeroValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.String: + return v.String() == "" + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface: + return v.IsNil() + default: + return false + } +} +``` \ No newline at end of file