f
This commit is contained in:
@@ -22,12 +22,12 @@ func ProcessFLXGHB4FRequest(ctx context.Context, params []byte, deps *processors
|
||||
}
|
||||
|
||||
// 使用 MD5 加密 name 和 idCard
|
||||
encryptedName := "MD5:" + huibo.MD5Encrypt(paramsDto.Name, deps.HuiboService.GetConfig().AppKey)
|
||||
encryptedIDCard := "MD5:" + huibo.MD5Encrypt(paramsDto.IDCard, deps.HuiboService.GetConfig().AppKey)
|
||||
// encryptedName := "MD5:" + huibo.MD5Encrypt(paramsDto.Name, deps.HuiboService.GetConfig().AppKey)
|
||||
// encryptedIDCard := "MD5:" + huibo.MD5Encrypt(paramsDto.IDCard, deps.HuiboService.GetConfig().AppKey)
|
||||
|
||||
reqdata := map[string]interface{}{
|
||||
"name": encryptedName,
|
||||
"idCard": encryptedIDCard,
|
||||
"name": paramsDto.Name,
|
||||
"idCard": paramsDto.IDCard,
|
||||
}
|
||||
|
||||
respBytes, err := deps.HuiboService.CallAPI2(ctx, "P_004_0271", reqdata)
|
||||
|
||||
90
internal/infrastructure/external/huibo/2.md
vendored
90
internal/infrastructure/external/huibo/2.md
vendored
@@ -1,90 +0,0 @@
|
||||
自然人公开涉诉信息查询接口文档
|
||||
接口编码:BHSC-P_004_0271
|
||||
版本:V1.0
|
||||
实施日期:2026-04-13
|
||||
适用场景:合作厂家接入中胜信用平台,用于个人涉诉案件查询(含失信、限高)
|
||||
1. 通信说明
|
||||
请求方式:HTTP POST
|
||||
数据格式:JSON
|
||||
编码格式:UTF-8
|
||||
安全要求:请求 IP 需提前绑定
|
||||
2. 请求信息
|
||||
请求地址
|
||||
http://host:port/api/data
|
||||
请求头(Header)
|
||||
表格
|
||||
参数名 含义 必填 类型 备注
|
||||
AppCode 接口授权码 Y String 接口服务商提供
|
||||
pcode 产品编码 Y String 固定值:P_004_0271
|
||||
请求体(Body)
|
||||
表格
|
||||
参数名 含义 必填 类型 说明
|
||||
name 姓名 Y String 支持明文 / MD5 加密
|
||||
idCard 身份证号 Y String 支持明文 / MD5 加密
|
||||
请求示例(明文)
|
||||
json
|
||||
{
|
||||
"idCard": "2103111*****0",
|
||||
"name": "*****"
|
||||
}
|
||||
请求示例(加密)
|
||||
json
|
||||
{
|
||||
"name": "MD5:3e29aae20b7b92775*****",
|
||||
"idCard": "MD5:a0c28f3a5a*****14"
|
||||
}
|
||||
3. 响应信息
|
||||
表格
|
||||
字段名 含义 类型 备注
|
||||
code 状态码 String 参考状态码说明
|
||||
message 描述信息 String -
|
||||
orderNo 订单号 String -
|
||||
pcode 产品编码 String 与请求一致
|
||||
param 请求参数 Object 原样返回
|
||||
charge 是否收费 Boolean true = 收费;false = 不收费
|
||||
time 响应时间戳 String 13 位毫秒
|
||||
data 业务数据 Object 涉诉 / 失信 / 限高数据
|
||||
响应示例
|
||||
json
|
||||
{
|
||||
"code": "100",
|
||||
"orderNo": "1361269246899077120",
|
||||
"charge": true,
|
||||
"data": {
|
||||
"ss": {
|
||||
"preservation": { "count": {} },
|
||||
"crc": 35****4186,
|
||||
"cases_tree": {},
|
||||
"administrative": {},
|
||||
"civil": {},
|
||||
"count": {},
|
||||
"implement": {},
|
||||
"criminal": {},
|
||||
"bankrupt": {}
|
||||
},
|
||||
"sxbzxr": [{}],
|
||||
"xgbzxr": []
|
||||
},
|
||||
"pcode": "P_004_0271",
|
||||
"param": null,
|
||||
"time": "1744593480538",
|
||||
"message": "查询成功"
|
||||
}
|
||||
data 节点说明
|
||||
ss:涉诉案件(民事 / 刑事 / 执行 / 行政 / 破产等)
|
||||
sxbzxr:失信被执行人
|
||||
xgbzxr:限制高消费
|
||||
4. 状态码说明
|
||||
表格
|
||||
状态码 描述
|
||||
100 查询成功
|
||||
101 参数错误
|
||||
103 账户不存在
|
||||
104 IP 限制
|
||||
105 账号已过期
|
||||
107 服务不存在
|
||||
108 产品通道已关闭
|
||||
109 账户资金不足
|
||||
110 查询成功,无数据
|
||||
500 未知请求错误
|
||||
要不要我帮你把这份接口直接写成可上线的小程序请求代码(含加密、header、异常捕获、状态码统一处理)?
|
||||
432
internal/infrastructure/external/huibo/crypto_test.go
vendored
Normal file
432
internal/infrastructure/external/huibo/crypto_test.go
vendored
Normal file
@@ -0,0 +1,432 @@
|
||||
package huibo
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 测试 MD5 加密(固定密文对比验证)
|
||||
func TestMD5Encrypt(t *testing.T) {
|
||||
appKey := "a6c9935e967894e731c62ecfcd9b7c95"
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
data string
|
||||
expected string // 固定密文
|
||||
}{
|
||||
{
|
||||
name: "姓名",
|
||||
data: "何志勇",
|
||||
expected: "64d4d5c6457026117a4911acf189e269", // 固定密文
|
||||
},
|
||||
{
|
||||
name: "身份证号",
|
||||
data: "452528197907133014",
|
||||
expected: "7c6cc77dabb83d95948904dba5a7219d", // 固定密文
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// 加密
|
||||
result := MD5Encrypt(tc.data, appKey)
|
||||
|
||||
// 最核心:对比是否和固定密文一致
|
||||
if result != tc.expected {
|
||||
t.Errorf("加密不匹配!\n明文:%s\n期望密文:%s\n实际密文:%s",
|
||||
tc.data, tc.expected, result)
|
||||
return
|
||||
}
|
||||
|
||||
// 打印成功日志
|
||||
t.Logf("✅ 校验成功\n明文:%s\n密文:%s", tc.data, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 测试 HMAC-SHA256 签名
|
||||
func TestHMACSHA256Base64(t *testing.T) {
|
||||
secret := "test_secret_key"
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
data string
|
||||
secret string
|
||||
}{
|
||||
{
|
||||
name: "简单字符串",
|
||||
data: "hello world",
|
||||
secret: secret,
|
||||
},
|
||||
{
|
||||
name: "JSON数据",
|
||||
data: `{"name":"张三","idCard":"110101199003072345"}`,
|
||||
secret: secret,
|
||||
},
|
||||
{
|
||||
name: "URL参数",
|
||||
data: "idCard=110101199003072345&name=张三&productCode=22089",
|
||||
secret: secret,
|
||||
},
|
||||
{
|
||||
name: "空字符串",
|
||||
data: "",
|
||||
secret: secret,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := HMACSHA256Base64(tc.data, tc.secret)
|
||||
|
||||
// 验证结果不为空
|
||||
if result == "" {
|
||||
t.Error("HMAC-SHA256签名结果为空")
|
||||
}
|
||||
|
||||
// 验证结果是有效的Base64
|
||||
_, err := base64.StdEncoding.DecodeString(result)
|
||||
if err != nil {
|
||||
t.Errorf("HMAC-SHA256结果不是有效的Base64: %v", err)
|
||||
}
|
||||
|
||||
// 验证相同输入产生相同输出
|
||||
result2 := HMACSHA256Base64(tc.data, tc.secret)
|
||||
if result != result2 {
|
||||
t.Error("相同输入产生的签名不一致")
|
||||
}
|
||||
|
||||
// 验证不同输入产生不同输出
|
||||
result3 := HMACSHA256Base64(tc.data+"x", tc.secret)
|
||||
if result == result3 {
|
||||
t.Error("不同输入产生的签名相同")
|
||||
}
|
||||
|
||||
t.Logf("数据: %s, 签名: %s", tc.data, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 测试 AES-GCM 加密解密
|
||||
func TestEncryptDecryptAESGCMBase64(t *testing.T) {
|
||||
// 生成一个有效的 Base64 编密的 AES 密钥
|
||||
key := make([]byte, 32) // AES-256
|
||||
for i := range key {
|
||||
key[i] = byte(i)
|
||||
}
|
||||
base64Key := base64.StdEncoding.EncodeToString(key)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
data string
|
||||
}{
|
||||
{
|
||||
name: "简单文本",
|
||||
data: "hello world",
|
||||
},
|
||||
{
|
||||
name: "中文文本",
|
||||
data: "你好世界",
|
||||
},
|
||||
{
|
||||
name: "JSON数据",
|
||||
data: `{"name":"张三","idCard":"110101199003072345"}`,
|
||||
},
|
||||
{
|
||||
name: "长文本",
|
||||
data: "这是一个很长的文本,用来测试加密解密功能是否正常工作。包含各种字符:123456789!@#$%^&*()_+-=[]{}|;':\",./<>?",
|
||||
},
|
||||
{
|
||||
name: "空字符串",
|
||||
data: "",
|
||||
},
|
||||
{
|
||||
name: "特殊字符",
|
||||
data: "\n\t\r\x00",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// 加密
|
||||
encrypted, err := EncryptAESGCMBase64(tc.data, base64Key)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
// 验证加密结果不为空
|
||||
if encrypted == "" {
|
||||
t.Error("加密结果为空")
|
||||
}
|
||||
|
||||
// 验证加密结果是有效的Base64
|
||||
_, err = base64.StdEncoding.DecodeString(encrypted)
|
||||
if err != nil {
|
||||
t.Errorf("加密结果不是有效的Base64: %v", err)
|
||||
}
|
||||
|
||||
// 验证加密结果与原文不同
|
||||
if encrypted == tc.data {
|
||||
t.Error("加密结果与原文相同")
|
||||
}
|
||||
|
||||
// 解密
|
||||
decrypted, err := DecryptAESGCMBase64(encrypted, base64Key)
|
||||
if err != nil {
|
||||
t.Fatalf("解密失败: %v", err)
|
||||
}
|
||||
|
||||
// 验证解密结果与原文一致
|
||||
if decrypted != tc.data {
|
||||
t.Errorf("解密结果不匹配,期望: %s, 实际: %s", tc.data, decrypted)
|
||||
}
|
||||
|
||||
t.Logf("原文: %s", tc.data)
|
||||
t.Logf("密文: %s", encrypted)
|
||||
t.Logf("解密: %s", decrypted)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 测试错误的密钥
|
||||
func TestEncryptDecryptWithWrongKey(t *testing.T) {
|
||||
// 生成两个不同的密钥
|
||||
key1 := make([]byte, 32)
|
||||
for i := range key1 {
|
||||
key1[i] = byte(i)
|
||||
}
|
||||
base64Key1 := base64.StdEncoding.EncodeToString(key1)
|
||||
|
||||
key2 := make([]byte, 32)
|
||||
for i := range key2 {
|
||||
key2[i] = byte(i + 1)
|
||||
}
|
||||
base64Key2 := base64.StdEncoding.EncodeToString(key2)
|
||||
|
||||
data := "sensitive data"
|
||||
|
||||
// 用密钥1加密
|
||||
encrypted, err := EncryptAESGCMBase64(data, base64Key1)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
|
||||
// 用密钥2解密
|
||||
decrypted, err := DecryptAESGCMBase64(encrypted, base64Key2)
|
||||
if err == nil {
|
||||
t.Error("用错误密钥解密应该返回错误")
|
||||
}
|
||||
|
||||
// 验证解密结果与原文不同(如果解密成功的话)
|
||||
if decrypted == data {
|
||||
t.Error("用错误密钥解密不应该得到正确结果")
|
||||
}
|
||||
|
||||
t.Logf("用错误密钥解密预期失败: %v", err)
|
||||
}
|
||||
|
||||
// 测试无效的 Base64 密钥
|
||||
func TestEncryptWithInvalidBase64Key(t *testing.T) {
|
||||
data := "test data"
|
||||
|
||||
invalidKeys := []string{
|
||||
"", // 空字符串
|
||||
"not_base64", // 非Base64
|
||||
"abc", // 解码后太短
|
||||
}
|
||||
|
||||
for _, invalidKey := range invalidKeys {
|
||||
_, err := EncryptAESGCMBase64(data, invalidKey)
|
||||
if err == nil {
|
||||
t.Errorf("使用无效密钥 %s 应该返回错误", invalidKey)
|
||||
}
|
||||
t.Logf("无效密钥 %s 预期失败: %v", invalidKey, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 测试解密无效数据
|
||||
func TestDecryptWithInvalidData(t *testing.T) {
|
||||
// 生成一个有效的密钥
|
||||
key := make([]byte, 32)
|
||||
for i := range key {
|
||||
key[i] = byte(i)
|
||||
}
|
||||
base64Key := base64.StdEncoding.EncodeToString(key)
|
||||
|
||||
invalidData := []string{
|
||||
"", // 空字符串
|
||||
"invalid_base64", // 非Base64
|
||||
"dGVzdA==", // 有效的Base64但不是AES-GCM数据
|
||||
"short", // 太短
|
||||
}
|
||||
|
||||
for _, data := range invalidData {
|
||||
_, err := DecryptAESGCMBase64(data, base64Key)
|
||||
if err == nil {
|
||||
t.Errorf("使用无效数据 %s 应该返回错误", data)
|
||||
}
|
||||
t.Logf("无效数据 %s 预期失败: %v", data, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 测试加密结果的唯一性
|
||||
func TestEncryptionUniqueness(t *testing.T) {
|
||||
// 生成一个有效的密钥
|
||||
key := make([]byte, 32)
|
||||
for i := range key {
|
||||
key[i] = byte(i)
|
||||
}
|
||||
base64Key := base64.StdEncoding.EncodeToString(key)
|
||||
|
||||
data := "same data"
|
||||
|
||||
// 加密多次
|
||||
results := make([]string, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
encrypted, err := EncryptAESGCMBase64(data, base64Key)
|
||||
if err != nil {
|
||||
t.Fatalf("第%d次加密失败: %v", i, err)
|
||||
}
|
||||
results[i] = encrypted
|
||||
}
|
||||
|
||||
// 验证每次加密结果都不同(因为包含随机IV)
|
||||
uniqueCount := 0
|
||||
for i := 0; i < len(results); i++ {
|
||||
isUnique := true
|
||||
for j := 0; j < len(results); j++ {
|
||||
if i != j && results[i] == results[j] {
|
||||
isUnique = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if isUnique {
|
||||
uniqueCount++
|
||||
}
|
||||
}
|
||||
|
||||
if uniqueCount != len(results) {
|
||||
t.Errorf("加密结果应该唯一,实际上只有%d个唯一结果,期望%d个", uniqueCount, len(results))
|
||||
}
|
||||
|
||||
t.Logf("生成了%d个不同的加密结果", uniqueCount)
|
||||
}
|
||||
|
||||
// 测试使用真实配置的加密解密
|
||||
func TestEncryptionWithRealConfig(t *testing.T) {
|
||||
// 使用配置文件中的真实AES密钥
|
||||
aesKey := "NQYN3YO+pb/GEcCBNX0ptMb7cUlnXSPvcX7VvNofBkc="
|
||||
appKey := "a6c9935e967894e731c62ecfcd9b7c95"
|
||||
|
||||
// 测试数据
|
||||
testData := `{"name":"张三","idCard":"110101199003072345","productCode":"22089"}`
|
||||
|
||||
t.Run("MD5加密", func(t *testing.T) {
|
||||
// 测试 MD5 加密
|
||||
md5Result := MD5Encrypt("张三", appKey)
|
||||
t.Logf("姓名 MD5: %s", md5Result)
|
||||
|
||||
md5Result2 := MD5Encrypt("110101199003072345", appKey)
|
||||
t.Logf("身份证号 MD5: %s", md5Result2)
|
||||
|
||||
// 验证格式
|
||||
if len(md5Result) != 32 {
|
||||
t.Errorf("MD5结果长度错误,期望32位,实际%d位", len(md5Result))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("HMAC-SHA256签名", func(t *testing.T) {
|
||||
// 生成排序后的参数
|
||||
sortedParam := "idCard=110101199003072345&name=张三&productCode=22089"
|
||||
signature := HMACSHA256Base64(sortedParam, aesKey)
|
||||
t.Logf("签名参数: %s", sortedParam)
|
||||
t.Logf("HMAC-SHA256签名: %s", signature)
|
||||
|
||||
// 验证格式
|
||||
_, err := base64.StdEncoding.DecodeString(signature)
|
||||
if err != nil {
|
||||
t.Errorf("签名不是有效的Base64: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("AES-GCM加密解密", func(t *testing.T) {
|
||||
// 测试 AES-GCM 加密
|
||||
encrypted, err := EncryptAESGCMBase64(testData, aesKey)
|
||||
if err != nil {
|
||||
t.Fatalf("加密失败: %v", err)
|
||||
}
|
||||
t.Logf("原始数据: %s", testData)
|
||||
t.Logf("加密结果: %s", encrypted)
|
||||
|
||||
// 测试解密
|
||||
decrypted, err := DecryptAESGCMBase64(encrypted, aesKey)
|
||||
if err != nil {
|
||||
t.Fatalf("解密失败: %v", err)
|
||||
}
|
||||
t.Logf("解密结果: %s", decrypted)
|
||||
|
||||
// 验证结果
|
||||
if decrypted != testData {
|
||||
t.Errorf("解密结果不匹配")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 基准测试
|
||||
func BenchmarkMD5Encrypt(b *testing.B) {
|
||||
data := "张三"
|
||||
appKey := "a6c9935e967894e731c62ecfcd9b7c95"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
MD5Encrypt(data, appKey)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHMACSHA256Base64(b *testing.B) {
|
||||
data := "idCard=110101199003072345&name=张三"
|
||||
secret := "a6c9935e967894e731c62ecfcd9b7c95"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
HMACSHA256Base64(data, secret)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncryptAESGCMBase64(b *testing.B) {
|
||||
data := `{"name":"张三","idCard":"110101199003072345","productCode":"22089"}`
|
||||
|
||||
// 生成密钥
|
||||
key := make([]byte, 32)
|
||||
for i := range key {
|
||||
key[i] = byte(i)
|
||||
}
|
||||
base64Key := base64.StdEncoding.EncodeToString(key)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
EncryptAESGCMBase64(data, base64Key)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecryptAESGCMBase64(b *testing.B) {
|
||||
data := `{"name":"张三","idCard":"110101199003072345","productCode":"22089"}`
|
||||
|
||||
// 生成密钥
|
||||
key := make([]byte, 32)
|
||||
for i := range key {
|
||||
key[i] = byte(i)
|
||||
}
|
||||
base64Key := base64.StdEncoding.EncodeToString(key)
|
||||
|
||||
// 先加密一次
|
||||
encrypted, err := EncryptAESGCMBase64(data, base64Key)
|
||||
if err != nil {
|
||||
b.Fatalf("预加密失败: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
DecryptAESGCMBase64(encrypted, base64Key)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user