添加文件: ymt_v3_2/crypto.go
This commit is contained in:
parent
f03c51655e
commit
db6a614a88
|
|
@ -0,0 +1,246 @@
|
|||
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
|
||||
Loading…
Reference in New Issue