package asign import ( "encoding/json" "mtp20access/global" "os/exec" "strings" "go.uber.org/zap" ) var appId = global.M2A_CONFIG.Asign.AppId var privateKey = global.M2A_CONFIG.Asign.PrivateKey // 签名规范: // // 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 // } type AsignData interface { AddPersonalUserData | CreateContractData | AddSignerData } // AsignRsp 爱签响应参数 type AsignRsp[T AsignData] struct { Code int `json:"code"` // 响应码,100000表示成功,其他表示异常 Msg string `json:"msg"` // 响应信息 Data T `json:"data"` // 响应数据 } // 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 // } 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 AddPersonalUserBy(account, name, idCard, mobile string, idCardType *int) (rspData AsignRsp[AddPersonalUserData], err error) { 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 } type CreateContractData struct { PreviewUrl string `json:"previewUrl"` // 合同预览链接 ContractFiles []interface{} `json:"contractFiles"` // 合同文件信息(文件名称,附件编号,页数) } /* * CreateContract 上传待签署文件 contractNo 合同ID,合同唯一编号 contractName 合同名称 templateNo 合同模板编号 * */ func CreateContract(contractNo, contractName, templateNo string) (rspData AsignRsp[CreateContractData], err error) { apiUrl := global.M2A_CONFIG.Asign.URL + "contract/createContract" // 构建请求数据结构 reqData := make(map[string]interface{}) reqData["contractNo"] = contractNo reqData["contractName"] = contractName reqData["signOrder"] = 1 // 1:无序签约(默认 reqData["templates"] = []map[string]string{ {"templateNo": templateNo}} // 合同模板编号 - 目前只支持一份合同签一份协议 reqData["notifyUrl"] = "" reqData["api"] = "createContract" 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 } // 合同用户信息 type SignUserData struct { Account string `json:"account"` // 用户唯一识别码 SignUrl string `json:"signUrl"` // 合同签署链接 PwdSignUrl string `json:"pwdSignUrl"` // 密码签署链接 SignOrder int `json:"signOrder"` // 顺序签约的序号 Name string `json:"name"` // 用户姓名 IdCard string `json:"idCard"` // 用户身份证 } // 添加签署方响应数据 type AddSignerData struct { ContractNo string `json:"contractNo"` // 合同编号 ContractName string `json:"contractName"` // 合同名称 ValidityTime string `json:"validityTime"` // 合同有效期 PreviewUrl string `json:"previewUrl"` // 合同预览链接 SignUser []SignUserData `json:"signUser"` // 合同用户信息 } /* * AddSigner 添加签署方 contractNo 合同ID,合同唯一编号 account 用户唯一识别码(UserID) * */ func AddSigner(contractNo, account string) (rspData AsignRsp[AddSignerData], err error) { apiUrl := global.M2A_CONFIG.Asign.URL + "contract/addSigner" // 构建请求数据结构 reqData := make(map[string]interface{}) reqData["contractNo"] = contractNo reqData["account"] = account reqData["signStrategyList"] = []map[string]int{ {"attachNo": 1, "locationMode": 4}} reqData["api"] = "addSigner" reqData["appId"] = appId reqData["appKey"] = privateKey reqData["apiUrl"] = apiUrl // 将请求数据转换为JSON字符串 reqJSON, err := json.Marshal(reqData) if err != nil { global.M2A_LOG.Error("[AddSigner] 构建请求参数失败", 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("[AddSigner] 请求失败", zap.Error(err)) return } // 结果 rspBody := string(output) if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil { global.M2A_LOG.Error("[AddSigner] 反序列化body失败", zap.Error(err)) return } return }