// File: ymt_v3/crypto.go ```go package ymt_v3 import ( "bytes" "crypto" "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "fmt" "io" "reflect" "sort" "strings" "time" "github.com/tjfoc/gmsm/sm4" ) const ( EncryptModeAES = "aes" EncryptModeSM4 = "sm4" ) func GenerateTimestamp() string { return time.Now().Format("2006-01-02 15:04:05") } func BuildSignString(appID, timestamp, ciphertext string) string { return appID + timestamp + ciphertext } func RSASign(signStr string, privateKeyPEM string) (string, error) { block, _ := pem.Decode([]byte(privateKeyPEM)) if block == nil { return "", fmt.Errorf("failed to decode PEM private key") } var privateKey *rsa.PrivateKey key, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { key, err = x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return "", fmt.Errorf("failed to parse private key: %v", err) } privateKey = key.(*rsa.PrivateKey) } else { var ok bool privateKey, ok = key.(*rsa.PrivateKey) if !ok { return "", fmt.Errorf("not a RSA private key") } } hash := sha256.Sum256([]byte(signStr)) signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:]) if err != nil { return "", fmt.Errorf("sign failed: %v", err) } return base64.StdEncoding.EncodeToString(signature), nil } func RSAVerify(signStr, signature string, publicKeyPEM string) error { block, _ := pem.Decode([]byte(publicKeyPEM)) if block == nil { return fmt.Errorf("failed to decode PEM public key") } pub, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return fmt.Errorf("failed to parse public key: %v", err) } publicKey, ok := pub.(*rsa.PublicKey) if !ok { return fmt.Errorf("not a RSA public key") } sigBytes, err := base64.StdEncoding.DecodeString(signature) if err != nil { return fmt.Errorf("failed to decode signature: %v", err) } hash := sha256.Sum256([]byte(signStr)) return rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hash[:], sigBytes) } func AESECBEncrypt(plaintext []byte, key []byte) (string, error) { block, err := aes.NewCipher(key) if err != nil { return "", fmt.Errorf("create AES cipher failed: %v", err) } padded := pkcs7Padding(plaintext, aes.BlockSize) ciphertext := make([]byte, len(padded)) for i := 0; i < len(padded); i += aes.BlockSize { block.Encrypt(ciphertext[i:i+aes.BlockSize], padded[i:i+aes.BlockSize]) } return base64.StdEncoding.EncodeToString(ciphertext), nil } func AESECBDecrypt(encryptedData string, key []byte) ([]byte, error) { ciphertext, err := base64.StdEncoding.DecodeString(encryptedData) if err != nil { return nil, fmt.Errorf("decode base64 failed: %v", err) } block, err := aes.NewCipher(key) if err != nil { return nil, fmt.Errorf("create AES cipher failed: %v", err) } if len(ciphertext)%aes.BlockSize != 0 { return nil, fmt.Errorf("ciphertext length is not multiple of block size") } plaintext := make([]byte, len(ciphertext)) for i := 0; i < len(ciphertext); i += aes.BlockSize { block.Decrypt(plaintext[i:i+aes.BlockSize], ciphertext[i:i+aes.BlockSize]) } plaintext, err = pkcs7UnPadding(plaintext) if err != nil { return nil, fmt.Errorf("remove padding failed: %v", err) } return plaintext, nil } func SM4CBCEncrypt(plaintext []byte, key []byte) (string, error) { if len(key) != 16 { return "", fmt.Errorf("SM4 key length must be 16 bytes") } block, err := sm4.NewCipher(key) if err != nil { return "", fmt.Errorf("create SM4 cipher failed: %v", err) } padded := pkcs7Padding(plaintext, block.BlockSize()) iv := make([]byte, block.BlockSize()) if _, err := io.ReadFull(rand.Reader, iv); err != nil { return "", fmt.Errorf("generate IV failed: %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 } func SM4CBCDecrypt(encryptedData string, key []byte) ([]byte, error) { if len(key) != 16 { return nil, fmt.Errorf("SM4 key length must be 16 bytes") } data, err := base64.StdEncoding.DecodeString(encryptedData) if err != nil { return nil, fmt.Errorf("decode base64 failed: %v", err) } block, err := sm4.NewCipher(key) if err != nil { return nil, fmt.Errorf("create SM4 cipher failed: %v", err) } blockSize := block.BlockSize() if len(data) < blockSize { return nil, fmt.Errorf("data length too short") } 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("remove padding failed: %v", 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) ([]byte, error) { length := len(data) if length == 0 { return nil, fmt.Errorf("data is empty") } unpadding := int(data[length-1]) if unpadding > length || unpadding == 0 { return nil, fmt.Errorf("invalid padding") } return data[:length-unpadding], nil } func StructToSortedJSON(v interface{}) (string, error) { val := reflect.ValueOf(v) if val.Kind() == reflect.Ptr { val = val.Elem() } if val.Kind() != reflect.Struct { return "", fmt.Errorf("expected struct, got %s", val.Kind()) } typ := val.Type() result := make(map[string]interface{}) for i := 0; i < val.NumField(); i++ { field := val.Field(i) fieldType := typ.Field(i) jsonTag := fieldType.Tag.Get("json") if jsonTag == "" || jsonTag == "-" { continue } name := jsonTag if idx := strings.Index(jsonTag, ","); idx != -1 { name = jsonTag[:idx] } if field.IsZero() { continue } result[name] = field.Interface() } keys := make([]string, 0, len(result)) for k := range result { keys = append(keys, k) } sort.Strings(keys) var buf bytes.Buffer buf.WriteString("{") for i, k := range keys { if i > 0 { buf.WriteString(",") } buf.WriteString(`"`) buf.WriteString(k) buf.WriteString(`":`) vBytes, err := json.Marshal(result[k]) if err != nil { return "", err } buf.Write(vBytes) } buf.WriteString("}") return buf.String(), nil } ```