asign.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package asign
  2. import (
  3. "bytes"
  4. "crypto"
  5. "crypto/md5"
  6. "crypto/rsa"
  7. "crypto/sha256"
  8. "crypto/x509"
  9. "encoding/base64"
  10. "encoding/hex"
  11. "encoding/json"
  12. "encoding/pem"
  13. "errors"
  14. "fmt"
  15. "io"
  16. "mtp20access/global"
  17. "net/http"
  18. "net/url"
  19. "os/exec"
  20. "strconv"
  21. "strings"
  22. "time"
  23. "go.uber.org/zap"
  24. )
  25. // 签名规范:
  26. //
  27. // 1、表单提交方式:form-data
  28. // 2、请求头部参数
  29. // 参数1:sign(签名值,具体算法参考一下的前面算法)
  30. // 参数2:timestamp(时间戳,13位)
  31. // 3、请求体参数:
  32. // 参数1:appId(appId值,每个接入者唯一一个)
  33. // 参数2:timestamp(时间戳,13位,与上述一致)
  34. // 参数3:bizData(json字符串,举个例子,比方说要传合同编号如:{"contractNo":"0001"})
  35. // 4、签名算法:
  36. // 4.1、将上述3所属的bizData(json字符串),按照阿拉伯字母排序(如:{"ba":1,"ac":2}--->{"ac":2,"ba":1}),
  37. // 4.2、将4.1排序后的字符串,将【bizData+md5(bizData)+ appId + timestatmp】拼接后利用RSA非对称加密算法(SHA1withRSA),计算出最后的签名sign,对其base64编码,放入head的key(sign)中。
  38. func getSignature(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) {
  39. timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10)
  40. privateKeyBlock, _ := pem.Decode([]byte(privateKeyPEM))
  41. // if privateKeyBlock == nil || privateKeyBlock.Type != "RSA PRIVATE KEY" {
  42. if privateKeyBlock == nil {
  43. err = errors.New("签名失败: Error decoding private key PEM")
  44. return
  45. }
  46. // 解析 PKCS#8 格式的私钥
  47. privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
  48. if err != nil {
  49. fmt.Println("Failed to parse private key:", err)
  50. return
  51. }
  52. // md5(bizData)
  53. m := md5.New()
  54. m.Write([]byte(bizData))
  55. bdMd5Hx := hex.EncodeToString(m.Sum(nil))
  56. // 待签内容
  57. message := bizData + bdMd5Hx + appId + timestamp
  58. // 使用私钥进行签名
  59. hashed := sha256.Sum256([]byte(message))
  60. signature, err := rsa.SignPKCS1v15(nil, privateKey, crypto.SHA256, hashed[:])
  61. if err != nil {
  62. fmt.Println("Error signing:", err)
  63. return
  64. }
  65. // fmt.Println(signature)
  66. signatureBase64 = base64.StdEncoding.EncodeToString(signature)
  67. return
  68. }
  69. type pySignReqData struct {
  70. ReqBodyData string `json:"reqBodyData"`
  71. Timestamp string `json:"timestamp"`
  72. AppId string `json:"appId"`
  73. AppKey string `json:"appKey"`
  74. }
  75. func getSignatureByPy(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) {
  76. // md5(bizData)
  77. m := md5.New()
  78. m.Write([]byte(bizData))
  79. bdMd5Hx := hex.EncodeToString(m.Sum(nil))
  80. timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10)
  81. // timestamp = "1691559290641"
  82. // 待签内容
  83. message := bizData + bdMd5Hx + appId + timestamp
  84. // 构建请求数据结构
  85. reqData := pySignReqData{
  86. ReqBodyData: message,
  87. Timestamp: timestamp,
  88. AppId: appId,
  89. AppKey: privateKeyPEM,
  90. }
  91. // 将请求数据转换为JSON字符串
  92. reqJSON, err := json.Marshal(reqData)
  93. if err != nil {
  94. global.M2A_LOG.Error("[getSignatureByPy] 构建请求参数失败", zap.Error(err))
  95. return
  96. }
  97. // 要执行的Python脚本命令
  98. pythonScriptPath := "./py/sign.py"
  99. // 创建一个命令对象
  100. cmd := exec.Command("py", pythonScriptPath)
  101. // 设置标准输入为JSON字符串
  102. cmd.Stdin = strings.NewReader(string(reqJSON))
  103. // 获取标准输出
  104. output, err := cmd.CombinedOutput()
  105. if err != nil {
  106. global.M2A_LOG.Error("[getSignatureByPy] 签名失败", zap.Error(err))
  107. return
  108. }
  109. // 获取签名结果
  110. signatureBase64 = string(output)
  111. return
  112. }
  113. // addPersonalUser 响应参数
  114. type AddPersonalUserRsp struct {
  115. Code int `json:"code"` // 响应码,100000表示成功,其他表示异常
  116. Msg string `json:"msg"` // 响应信息
  117. Data AddPersonalUserData `json:"data"` // 响应数据
  118. }
  119. type AddPersonalUserData struct {
  120. SealNo string `json:"sealNo"` // 默认印章编号
  121. }
  122. // https://preweb.asign.cn/platform/openDoc/docDetail?mid=addPersonalUser
  123. // 添加个人用户(https://{host}/user/addPersonalUser)
  124. // 错误码 错误描述
  125. // 100021 用户已存在
  126. // 100156 手机号码格式错误
  127. // 100157 邮箱格式错误
  128. // 100571 参数错误,唯一识别码Account为空
  129. // 100577 参数错误,{param}长度超过限制:{length}
  130. // 100579 参数错误,{param}不能为空
  131. // 100598 参数错误,身份证号码格式不正确
  132. // 100639 参数错误,名字点号格式不正确
  133. func AddPersonalUser(account, name, idCard, mobile string, idCardType *int) (rspData AddPersonalUserRsp, err error) {
  134. apiUrl := global.M2A_CONFIG.Asign.URL + "user/addPersonalUser"
  135. appId := "290912417"
  136. privateKey := `MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAkMD+72J6iAF0ZNV+3t628lsRHfJ80nKZWK5/C7Pg+AZmOIzJlwHsKhRzCvxoxqYHQprhiFzW9l73v9vD9l1JYwIDAQABAkBVijccr01JYdKuY5t9iI8D2NzcnZc1pZMI3NUmzT18Uyg7b9CUvGHlLeg/gdT4QtVd7wIzHYCY4letEcEMh54BAiEAwzNWusj5XiLmty7PI0Hbakx4HtcND1+P0UHLEWqWOuECIQC91zQuL7nStgGzT3HvaeBB5Ouapa39fHRm2nCjHaxwwwIgRR2XdvmUOj23XWMomr5F14SN/7V7fVcD0D8wjNElsmECIDYavV5kb7tj7/wgqkInlKhzC8rZaUsTS0F9BBkY/eptAiAQJ8Saz8YlMIESdHMxANGSog01fECbcZqLFMuNf8SorA==`
  137. // 请求参数
  138. params := make(map[string]interface{})
  139. params["account"] = account
  140. params["name"] = name
  141. params["idCard"] = idCard
  142. params["idCardType"] = 1
  143. if idCardType != nil {
  144. params["idCardType"] = *idCardType
  145. }
  146. params["mobile"] = mobile
  147. // 用户实名认证模式为强制认证时,需要选择认证方法:
  148. // 1:身份证二要素认证
  149. // 2:运营商三要素认证
  150. // 3:银行卡四要素认证
  151. params["identifyType"] = 2
  152. params["identifyMobile"] = mobile
  153. params["isNotice"] = 1
  154. bizData, err := json.Marshal(params)
  155. if err != nil {
  156. global.M2A_LOG.Error("[AddPersonalUser] 构建请求参数失败", zap.Error(err))
  157. return
  158. }
  159. global.M2A_LOG.Info("[AddPersonalUser] 构建请求参数", zap.Any("params", string(bizData)))
  160. // 签名
  161. sign, timestamp, err := getSignatureByPy(string(bizData), appId, privateKey)
  162. sign = strings.Replace(sign, "\r\n", "", -1)
  163. sign = strings.Replace(sign, "\r", "", -1)
  164. if err != nil {
  165. global.M2A_LOG.Error("[AddPersonalUser] 签名失败", zap.Error(err))
  166. return
  167. }
  168. global.M2A_LOG.Info("[AddPersonalUser] 签名", zap.Any("sign", sign))
  169. // 构建form-data请求参数
  170. formValues := url.Values{}
  171. formValues.Set("appId", appId)
  172. formValues.Set("timestamp", timestamp)
  173. formValues.Set("bizData", string(bizData))
  174. // 构建请求
  175. req, err := http.NewRequest("POST", apiUrl, bytes.NewReader([]byte(formValues.Encode())))
  176. // 设置请求头
  177. req.Header.Set("sign", sign)
  178. req.Header.Set("timestamp", timestamp)
  179. req.Header.Set("Content-Type", "multipart/form-data; charset=UTF-8")
  180. req.Header.Set("Accept", "*/*")
  181. client := &http.Client{}
  182. rsp, err := client.Do(req)
  183. if err != nil {
  184. global.M2A_LOG.Error("[AddPersonalUser] 请求失败", zap.Error(err))
  185. return
  186. }
  187. defer rsp.Body.Close()
  188. body, err := io.ReadAll(rsp.Body)
  189. if err != nil {
  190. global.M2A_LOG.Error("[AddPersonalUser] 获取body失败", zap.Error(err))
  191. return
  192. }
  193. if err = json.Unmarshal(body, &rspData); err != nil {
  194. global.M2A_LOG.Error("[AddPersonalUser] 反序列化body失败", zap.Error(err))
  195. return
  196. }
  197. return
  198. }
  199. // https://preweb.asign.cn/platform/openDoc/docDetail?mid=addPersonalUser
  200. // 添加个人用户(https://{host}/user/addPersonalUser)
  201. // 错误码 错误描述
  202. // 100021 用户已存在
  203. // 100156 手机号码格式错误
  204. // 100157 邮箱格式错误
  205. // 100571 参数错误,唯一识别码Account为空
  206. // 100577 参数错误,{param}长度超过限制:{length}
  207. // 100579 参数错误,{param}不能为空
  208. // 100598 参数错误,身份证号码格式不正确
  209. // 100639 参数错误,名字点号格式不正确
  210. func AddPersonalUserBy(account, name, idCard, mobile string, idCardType *int) (rspData AddPersonalUserRsp, err error) {
  211. appId := global.M2A_CONFIG.Asign.AppId
  212. privateKey := global.M2A_CONFIG.Asign.PrivateKey
  213. apiUrl := global.M2A_CONFIG.Asign.URL + "user/addPersonalUser"
  214. // 构建请求数据结构
  215. reqData := make(map[string]interface{})
  216. reqData["account"] = account
  217. reqData["name"] = name
  218. reqData["mobile"] = mobile
  219. reqData["idCard"] = idCard
  220. reqData["idCardType"] = 1
  221. if idCardType != nil {
  222. reqData["idCardType"] = *idCardType
  223. }
  224. reqData["api"] = "addPerson"
  225. reqData["appId"] = appId
  226. reqData["appKey"] = privateKey
  227. reqData["apiUrl"] = apiUrl
  228. // 将请求数据转换为JSON字符串
  229. reqJSON, err := json.Marshal(reqData)
  230. if err != nil {
  231. global.M2A_LOG.Error("[AddPersonalUserBy] 构建请求参数失败", zap.Error(err))
  232. return
  233. }
  234. // 要执行的Python脚本命令
  235. pythonScriptPath := "./py/Enter.py"
  236. // 创建一个命令对象
  237. cmd := exec.Command("py", pythonScriptPath)
  238. // 设置标准输入为JSON字符串
  239. cmd.Stdin = strings.NewReader(string(reqJSON))
  240. // 获取标准输出
  241. output, err := cmd.CombinedOutput()
  242. if err != nil {
  243. global.M2A_LOG.Error("[AddPersonalUserBy] 请求失败", zap.Error(err))
  244. return
  245. }
  246. // 结果
  247. rspBody := string(output)
  248. if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
  249. global.M2A_LOG.Error("[AddPersonalUserBy] 反序列化body失败", zap.Error(err))
  250. return
  251. }
  252. return
  253. }