package ymt_v3_2 import ( "bytes" "crypto" "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "errors" "fmt" "sort" "strings" ) func parsePrivateKey(pemData string) (*rsa.PrivateKey, error) { block, _ := pem.Decode([]byte(pemData)) if block == nil { return nil, errors.New("failed to decode PEM block") } key, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { key, err = x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return nil, fmt.Errorf("parse private key: %w", err) } } rsaKey, ok := key.(*rsa.PrivateKey) if !ok { return nil, errors.New("not an RSA private key") } return rsaKey, nil } func parsePublicKey(pemData string) (*rsa.PublicKey, error) { block, _ := pem.Decode([]byte(pemData)) if block == nil { return nil, errors.New("failed to decode PEM block") } key, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, fmt.Errorf("parse public key: %w", err) } rsaKey, ok := key.(*rsa.PublicKey) if !ok { return nil, errors.New("not an RSA public key") } return rsaKey, nil } func marshalSortedNoZero(v interface{}) ([]byte, error) { data, err := json.Marshal(v) if err != nil { return nil, err } var raw map[string]interface{} if err := json.Unmarshal(data, &raw); err != nil { return nil, err } filtered := removeZeroValues(raw) keys := make([]string, 0, len(filtered)) for k := range filtered { keys = append(keys, k) } sort.Strings(keys) var buf bytes.Buffer buf.WriteByte('{') for i, k := range keys { if i > 0 { buf.WriteByte(',') } keyBytes, _ := json.Marshal(k) buf.Write(keyBytes) buf.WriteByte(':') valBytes, err := json.Marshal(filtered[k]) if err != nil { return nil, err } buf.Write(valBytes) } buf.WriteByte('}') return buf.Bytes(), nil } func removeZeroValues(m map[string]interface{}) map[string]interface{} { res := make(map[string]interface{}) for k, v := range m { if isZeroValue(v) { continue } if sub, ok := v.(map[string]interface{}); ok { subFiltered := removeZeroValues(sub) if len(subFiltered) > 0 { res[k] = subFiltered } } else { res[k] = v } } return res } func isZeroValue(v interface{}) bool { if v == nil { return true } switch val := v.(type) { case string: return val == "" case float64: return val == 0 case bool: return !val case []interface{}: return len(val) == 0 case map[string]interface{}: return len(val) == 0 default: return false } } func aesEncryptECB(plaintext, key []byte) (string, error) { block, err := aes.NewCipher(key) if err != nil { return "", err } blockSize := block.BlockSize() plaintext = pkcs7Padding(plaintext, blockSize) ciphertext := make([]byte, len(plaintext)) for i := 0; i < len(plaintext); i += blockSize { block.Encrypt(ciphertext[i:i+blockSize], plaintext[i:i+blockSize]) } return base64.StdEncoding.EncodeToString(ciphertext), nil } func aesDecryptECB(ciphertext string, key []byte) ([]byte, error) { data, err := base64.StdEncoding.DecodeString(ciphertext) if err != nil { return nil, err } block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() if len(data)%blockSize != 0 { return nil, errors.New("ciphertext is not a multiple of the block size") } plaintext := make([]byte, len(data)) for i := 0; i < len(data); i += blockSize { block.Decrypt(plaintext[i:i+blockSize], data[i:i+blockSize]) } plaintext, err = pkcs7Unpadding(plaintext, blockSize) if err != nil { return nil, err } return plaintext, nil } func pkcs7Padding(data []byte, blockSize int) []byte { padding := blockSize - len(data)%blockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(data, padtext...) } func pkcs7Unpadding(data []byte, blockSize int) ([]byte, error) { length := len(data) if length == 0 { return nil, errors.New("empty data") } padding := int(data[length-1]) if padding > blockSize || padding == 0 { return nil, errors.New("invalid padding") } for i := length - padding; i < length; i++ { if data[i] != byte(padding) { return nil, errors.New("invalid padding") } } return data[:length-padding], nil } func rsaSign(data []byte, privateKey *rsa.PrivateKey) (string, error) { hashed := sha256.Sum256(data) signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:]) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(signature), nil } func rsaVerify(data []byte, sign string, publicKey *rsa.PublicKey) error { signBytes, err := base64.StdEncoding.DecodeString(sign) if err != nil { return err } hashed := sha256.Sum256(data) return rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed[:], signBytes) } func (c *Client) VerifyCallbackSign(appID, timestamp, sign string, callbackData map[string]interface{}) error { filtered := removeZeroValues(callbackData) keys := make([]string, 0, len(filtered)) for k := range filtered { keys = append(keys, k) } sort.Strings(keys) var buf bytes.Buffer buf.WriteByte('{') for i, k := range keys { if i > 0 { buf.WriteByte(',') } keyBytes, _ := json.Marshal(k) buf.Write(keyBytes) buf.WriteByte(':') valBytes, err := json.Marshal(filtered[k]) if err != nil { return err } buf.Write(valBytes) } buf.WriteByte('}') plaintext := buf.Bytes() ciphertext, err := aesEncryptECB(plaintext, c.aesKey) if err != nil { return err } signStr := appID + timestamp + ciphertext return rsaVerify([]byte(signStr), sign, c.publicKey) } var _ = cipher.NewCBCEncrypter var _ = strings.ReplaceAll