|
|
@@ -430,7 +430,7 @@ func WebPayHwXiaoQiNotify(c *gin.Context) {
|
|
|
// 使用公钥解密(对应 PHP 的 openssl_public_decrypt)
|
|
|
func DecryptDataToMap(encrypDataBase64 string, publicKeyStr string) (map[string]string, error) {
|
|
|
// 1. Base64 解码得到 raw_encryp_data
|
|
|
- rawEncrypData, err := base64.StdEncoding.DecodeString(encrypDataBase64)
|
|
|
+ rawEncrypData, err := decodeBase64Compat(encrypDataBase64)
|
|
|
if err != nil {
|
|
|
return nil, fmt.Errorf("base64解码失败: %v", err)
|
|
|
}
|
|
|
@@ -452,20 +452,109 @@ func rsaPublicDecrypt(ciphertext []byte, publicKeyStr string) ([]byte, error) {
|
|
|
return nil, fmt.Errorf("解析公钥失败: %v", err)
|
|
|
}
|
|
|
|
|
|
- // 创建一个假的私钥对象,只填充公钥部分
|
|
|
- fakePrivateKey := &rsa.PrivateKey{
|
|
|
- PublicKey: *publicKey,
|
|
|
- D: new(big.Int).SetInt64(0), // 占位符
|
|
|
+ keySize := (publicKey.N.BitLen() + 7) / 8
|
|
|
+ if keySize == 0 {
|
|
|
+ return nil, fmt.Errorf("无效RSA公钥")
|
|
|
}
|
|
|
|
|
|
- // 使用私钥解密(实际会使用公钥)
|
|
|
- // 注意:这个方法可能在某些 Go 版本中不工作
|
|
|
- plaintext, err := rsa.DecryptPKCS1v15(nil, fakePrivateKey, ciphertext)
|
|
|
- if err != nil {
|
|
|
- return nil, fmt.Errorf("解密失败: %v", err)
|
|
|
+ if len(ciphertext)%keySize != 0 {
|
|
|
+ return nil, fmt.Errorf("密文长度非法: len=%d keySize=%d", len(ciphertext), keySize)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 兼容长消息分段密文(每段一个 RSA block)。
|
|
|
+ plainData := make([]byte, 0, len(ciphertext))
|
|
|
+ e := big.NewInt(int64(publicKey.E))
|
|
|
+ for offset := 0; offset < len(ciphertext); offset += keySize {
|
|
|
+ block := ciphertext[offset : offset+keySize]
|
|
|
+ c := new(big.Int).SetBytes(block)
|
|
|
+ if c.Cmp(publicKey.N) > 0 {
|
|
|
+ return nil, fmt.Errorf("密文块超出模数范围")
|
|
|
+ }
|
|
|
+
|
|
|
+ m := new(big.Int).Exp(c, e, publicKey.N)
|
|
|
+ em := leftPadToSize(m.Bytes(), keySize)
|
|
|
+ plainBlock, err := unpadPKCS1v15ForPublicDecrypt(em)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("块解密失败: %v", err)
|
|
|
+ }
|
|
|
+ plainData = append(plainData, plainBlock...)
|
|
|
+ }
|
|
|
+
|
|
|
+ return plainData, nil
|
|
|
+}
|
|
|
+
|
|
|
+func decodeBase64Compat(raw string) ([]byte, error) {
|
|
|
+ // form-urlencoded 场景下,+ 可能被自动转为空格。
|
|
|
+ s := strings.TrimSpace(strings.ReplaceAll(raw, " ", "+"))
|
|
|
+ if s == "" {
|
|
|
+ return nil, fmt.Errorf("空字符串")
|
|
|
+ }
|
|
|
+
|
|
|
+ encodings := []*base64.Encoding{
|
|
|
+ base64.StdEncoding,
|
|
|
+ base64.RawStdEncoding,
|
|
|
+ base64.URLEncoding,
|
|
|
+ base64.RawURLEncoding,
|
|
|
+ }
|
|
|
+
|
|
|
+ var lastErr error
|
|
|
+ for _, enc := range encodings {
|
|
|
+ data, err := enc.DecodeString(s)
|
|
|
+ if err == nil {
|
|
|
+ return data, nil
|
|
|
+ }
|
|
|
+ lastErr = err
|
|
|
+ }
|
|
|
+ return nil, lastErr
|
|
|
+}
|
|
|
+
|
|
|
+func leftPadToSize(src []byte, size int) []byte {
|
|
|
+ if len(src) >= size {
|
|
|
+ return src
|
|
|
+ }
|
|
|
+ dst := make([]byte, size)
|
|
|
+ copy(dst[size-len(src):], src)
|
|
|
+ return dst
|
|
|
+}
|
|
|
+
|
|
|
+// unpadPKCS1v15ForPublicDecrypt 兼容 openssl_public_decrypt 的 PKCS#1 v1.5 去填充。
|
|
|
+func unpadPKCS1v15ForPublicDecrypt(em []byte) ([]byte, error) {
|
|
|
+ if len(em) < 11 {
|
|
|
+ return nil, fmt.Errorf("EM 长度过短")
|
|
|
+ }
|
|
|
+ if em[0] != 0x00 {
|
|
|
+ return nil, fmt.Errorf("EM 格式错误: 缺少前导0x00")
|
|
|
}
|
|
|
|
|
|
- return plaintext, nil
|
|
|
+ switch em[1] {
|
|
|
+ case 0x01:
|
|
|
+ i := 2
|
|
|
+ for i < len(em) && em[i] == 0xFF {
|
|
|
+ i++
|
|
|
+ }
|
|
|
+ if i < 10 {
|
|
|
+ return nil, fmt.Errorf("PKCS#1 type1 填充长度不足")
|
|
|
+ }
|
|
|
+ if i >= len(em) || em[i] != 0x00 {
|
|
|
+ return nil, fmt.Errorf("PKCS#1 type1 分隔符缺失")
|
|
|
+ }
|
|
|
+ return em[i+1:], nil
|
|
|
+ case 0x02:
|
|
|
+ // 某些渠道可能走普通公钥加密块,这里也做兼容。
|
|
|
+ i := 2
|
|
|
+ for i < len(em) && em[i] != 0x00 {
|
|
|
+ i++
|
|
|
+ }
|
|
|
+ if i < 10 {
|
|
|
+ return nil, fmt.Errorf("PKCS#1 type2 填充长度不足")
|
|
|
+ }
|
|
|
+ if i >= len(em) || em[i] != 0x00 {
|
|
|
+ return nil, fmt.Errorf("PKCS#1 type2 分隔符缺失")
|
|
|
+ }
|
|
|
+ return em[i+1:], nil
|
|
|
+ default:
|
|
|
+ return nil, fmt.Errorf("不支持的填充类型: 0x%02x", em[1])
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 解析查询字符串为 map
|
|
|
@@ -503,7 +592,7 @@ func VerifySignature(params map[string]string, publicKeyStr string) (bool, error
|
|
|
}
|
|
|
|
|
|
// base64_decode 得到 raw_sign_data
|
|
|
- rawSignData, err := base64.StdEncoding.DecodeString(signDataBase64)
|
|
|
+ rawSignData, err := decodeBase64Compat(signDataBase64)
|
|
|
if err != nil {
|
|
|
return false, fmt.Errorf("base64解码失败: %v", err)
|
|
|
}
|