package asign import ( "bytes" "crypto" "crypto/md5" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "errors" "fmt" "io" "mtp20access/global" "net/http" "net/url" "os/exec" "strconv" "strings" "time" "go.uber.org/zap" ) // 签名规范: // // 1、表单提交方式:form-data // 2、请求头部参数 // 参数1:sign(签名值,具体算法参考一下的前面算法) // 参数2:timestamp(时间戳,13位) // 3、请求体参数: // 参数1:appId(appId值,每个接入者唯一一个) // 参数2:timestamp(时间戳,13位,与上述一致) // 参数3:bizData(json字符串,举个例子,比方说要传合同编号如:{"contractNo":"0001"}) // 4、签名算法: // 4.1、将上述3所属的bizData(json字符串),按照阿拉伯字母排序(如:{"ba":1,"ac":2}--->{"ac":2,"ba":1}), // 4.2、将4.1排序后的字符串,将【bizData+md5(bizData)+ appId + timestatmp】拼接后利用RSA非对称加密算法(SHA1withRSA),计算出最后的签名sign,对其base64编码,放入head的key(sign)中。 func getSignature(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) { timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10) privateKeyBlock, _ := pem.Decode([]byte(privateKeyPEM)) // if privateKeyBlock == nil || privateKeyBlock.Type != "RSA PRIVATE KEY" { if privateKeyBlock == nil { err = errors.New("签名失败: Error decoding private key PEM") return } // 解析 PKCS#8 格式的私钥 privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes) if err != nil { fmt.Println("Failed to parse private key:", err) return } // md5(bizData) m := md5.New() m.Write([]byte(bizData)) bdMd5Hx := hex.EncodeToString(m.Sum(nil)) // 待签内容 message := bizData + bdMd5Hx + appId + timestamp // 使用私钥进行签名 hashed := sha256.Sum256([]byte(message)) signature, err := rsa.SignPKCS1v15(nil, privateKey, crypto.SHA256, hashed[:]) if err != nil { fmt.Println("Error signing:", err) return } // fmt.Println(signature) signatureBase64 = base64.StdEncoding.EncodeToString(signature) return } type pySignReqData struct { ReqBodyData string `json:"reqBodyData"` Timestamp string `json:"timestamp"` AppId string `json:"appId"` AppKey string `json:"appKey"` } func getSignatureByPy(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) { // md5(bizData) m := md5.New() m.Write([]byte(bizData)) bdMd5Hx := hex.EncodeToString(m.Sum(nil)) timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10) // timestamp = "1691559290641" // 待签内容 message := bizData + bdMd5Hx + appId + timestamp // 构建请求数据结构 reqData := pySignReqData{ ReqBodyData: message, Timestamp: timestamp, AppId: appId, AppKey: privateKeyPEM, } // 将请求数据转换为JSON字符串 reqJSON, err := json.Marshal(reqData) if err != nil { global.M2A_LOG.Error("[getSignatureByPy] 构建请求参数失败", zap.Error(err)) return } // 要执行的Python脚本命令 pythonScriptPath := "./py/sign.py" // 创建一个命令对象 cmd := exec.Command("py", pythonScriptPath) // 设置标准输入为JSON字符串 cmd.Stdin = strings.NewReader(string(reqJSON)) // 获取标准输出 output, err := cmd.CombinedOutput() if err != nil { global.M2A_LOG.Error("[getSignatureByPy] 签名失败", zap.Error(err)) return } // 获取签名结果 signatureBase64 = string(output) return } // addPersonalUser 响应参数 type AddPersonalUserRsp struct { Code int `json:"code"` // 响应码,100000表示成功,其他表示异常 Msg string `json:"msg"` // 响应信息 Data AddPersonalUserData `json:"data"` // 响应数据 } type AddPersonalUserData struct { SealNo string `json:"sealNo"` // 默认印章编号 } // https://preweb.asign.cn/platform/openDoc/docDetail?mid=addPersonalUser // 添加个人用户(https://{host}/user/addPersonalUser) // 错误码 错误描述 // 100021 用户已存在 // 100156 手机号码格式错误 // 100157 邮箱格式错误 // 100571 参数错误,唯一识别码Account为空 // 100577 参数错误,{param}长度超过限制:{length} // 100579 参数错误,{param}不能为空 // 100598 参数错误,身份证号码格式不正确 // 100639 参数错误,名字点号格式不正确 func AddPersonalUser(account, name, idCard, mobile string, idCardType *int) (rspData AddPersonalUserRsp, err error) { apiUrl := global.M2A_CONFIG.Asign.URL + "user/addPersonalUser" appId := "290912417" privateKey := `MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAkMD+72J6iAF0ZNV+3t628lsRHfJ80nKZWK5/C7Pg+AZmOIzJlwHsKhRzCvxoxqYHQprhiFzW9l73v9vD9l1JYwIDAQABAkBVijccr01JYdKuY5t9iI8D2NzcnZc1pZMI3NUmzT18Uyg7b9CUvGHlLeg/gdT4QtVd7wIzHYCY4letEcEMh54BAiEAwzNWusj5XiLmty7PI0Hbakx4HtcND1+P0UHLEWqWOuECIQC91zQuL7nStgGzT3HvaeBB5Ouapa39fHRm2nCjHaxwwwIgRR2XdvmUOj23XWMomr5F14SN/7V7fVcD0D8wjNElsmECIDYavV5kb7tj7/wgqkInlKhzC8rZaUsTS0F9BBkY/eptAiAQJ8Saz8YlMIESdHMxANGSog01fECbcZqLFMuNf8SorA==` // 请求参数 params := make(map[string]interface{}) params["account"] = account params["name"] = name params["idCard"] = idCard params["idCardType"] = 1 if idCardType != nil { params["idCardType"] = *idCardType } params["mobile"] = mobile // 用户实名认证模式为强制认证时,需要选择认证方法: // 1:身份证二要素认证 // 2:运营商三要素认证 // 3:银行卡四要素认证 params["identifyType"] = 2 params["identifyMobile"] = mobile params["isNotice"] = 1 bizData, err := json.Marshal(params) if err != nil { global.M2A_LOG.Error("[AddPersonalUser] 构建请求参数失败", zap.Error(err)) return } global.M2A_LOG.Info("[AddPersonalUser] 构建请求参数", zap.Any("params", string(bizData))) // 签名 sign, timestamp, err := getSignatureByPy(string(bizData), appId, privateKey) sign = strings.Replace(sign, "\r\n", "", -1) sign = strings.Replace(sign, "\r", "", -1) if err != nil { global.M2A_LOG.Error("[AddPersonalUser] 签名失败", zap.Error(err)) return } global.M2A_LOG.Info("[AddPersonalUser] 签名", zap.Any("sign", sign)) // 构建form-data请求参数 formValues := url.Values{} formValues.Set("appId", appId) formValues.Set("timestamp", timestamp) formValues.Set("bizData", string(bizData)) // 构建请求 req, err := http.NewRequest("POST", apiUrl, bytes.NewReader([]byte(formValues.Encode()))) // 设置请求头 req.Header.Set("sign", sign) req.Header.Set("timestamp", timestamp) req.Header.Set("Content-Type", "multipart/form-data; charset=UTF-8") req.Header.Set("Accept", "*/*") client := &http.Client{} rsp, err := client.Do(req) if err != nil { global.M2A_LOG.Error("[AddPersonalUser] 请求失败", zap.Error(err)) return } defer rsp.Body.Close() body, err := io.ReadAll(rsp.Body) if err != nil { global.M2A_LOG.Error("[AddPersonalUser] 获取body失败", zap.Error(err)) return } if err = json.Unmarshal(body, &rspData); err != nil { global.M2A_LOG.Error("[AddPersonalUser] 反序列化body失败", zap.Error(err)) return } return } // https://preweb.asign.cn/platform/openDoc/docDetail?mid=addPersonalUser // 添加个人用户(https://{host}/user/addPersonalUser) // 错误码 错误描述 // 100021 用户已存在 // 100156 手机号码格式错误 // 100157 邮箱格式错误 // 100571 参数错误,唯一识别码Account为空 // 100577 参数错误,{param}长度超过限制:{length} // 100579 参数错误,{param}不能为空 // 100598 参数错误,身份证号码格式不正确 // 100639 参数错误,名字点号格式不正确 func AddPersonalUserBy(account, name, idCard, mobile string, idCardType *int) (rspData AddPersonalUserRsp, err error) { appId := global.M2A_CONFIG.Asign.AppId privateKey := global.M2A_CONFIG.Asign.PrivateKey apiUrl := global.M2A_CONFIG.Asign.URL + "user/addPersonalUser" // 构建请求数据结构 reqData := make(map[string]interface{}) reqData["account"] = account reqData["name"] = name reqData["mobile"] = mobile reqData["idCard"] = idCard reqData["idCardType"] = 1 if idCardType != nil { reqData["idCardType"] = *idCardType } reqData["api"] = "addPerson" reqData["appId"] = appId reqData["appKey"] = privateKey reqData["apiUrl"] = apiUrl // 将请求数据转换为JSON字符串 reqJSON, err := json.Marshal(reqData) if err != nil { global.M2A_LOG.Error("[AddPersonalUserBy] 构建请求参数失败", zap.Error(err)) return } // 要执行的Python脚本命令 pythonScriptPath := "./py/Enter.py" // 创建一个命令对象 cmd := exec.Command("py", pythonScriptPath) // 设置标准输入为JSON字符串 cmd.Stdin = strings.NewReader(string(reqJSON)) // 获取标准输出 output, err := cmd.CombinedOutput() if err != nil { global.M2A_LOG.Error("[AddPersonalUserBy] 请求失败", zap.Error(err)) return } // 结果 rspBody := string(output) if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil { global.M2A_LOG.Error("[AddPersonalUserBy] 反序列化body失败", zap.Error(err)) return } return }