网站建设工作会议召开,jsp mysql 网站开发,龙港网站建设,wordpress图片文章一、过程
1.调用第三方接口前#xff0c;一般需要按规则将参数按key1value1key2value2 阿斯克码排序,sign参数不参与加密
2.将排序并连接好的参数字符串通过我方的私钥证书#xff08;.pem#xff09;进行加密得到加密串#xff0c;当然加密得到的是 []byte 字节流一般需要按规则将参数按key1value1key2value2 阿斯克码排序,sign参数不参与加密
2.将排序并连接好的参数字符串通过我方的私钥证书.pem进行加密得到加密串当然加密得到的是 []byte 字节流需要将字节流转换成base64字符串
3.将加密字符串赋值给sign参数并与其他加密参数一起通过post application/x-www-form-urlencoded 请求第三方接口。 当然第三方与我方对接的时会要求我方提供公约证书给到他们用以拿到我方请求后对我方请求参数进行延签。
4.同理我方请求第三方后会返回数据给到我方以及第三方会请求我们的回调接口那么就需要对第三方返回的参数进行验签而验签同理需要将加密参数提取出来并进行排序需要明白的是此时解签我们需要用到 第三方的公钥 进行验签
5.其中需要注意的是我所了解的目前golang 加密和解密一般都是用的pem证书而大部分第三方提供的证书是 .pfx .cer ,所以我需要在win上安装openssl工具然后将 .pfx .cer转成 .pem证书
证书转换方式如下
1. 下载Win64OpenSSL-3_1_2.exe 并安装下载https://slproweb.com/products/Win32OpenSSL.html
2. 环境变量 Path 加入 D:\OpenSSL-Win64\bin
3. amd 下命令 openssl version 验证是否安装成功4. 通过命令解析生成 公私钥 uat.pfx openssl pkcs12 -in xxxx.pfx -nodes -out server.pem #生成为原生格式pem 私钥 ******openssl rsa -in server.pem -out server.key #生成为rsa格式私钥文件openssl x509 -in server.pem -out server.crtopenssl pkcs12 -in xxxx.pfx -clcerts -nokeys -out key.certopenssl x509 -inform der -in xxx.cer -out xxx.pem #公钥pem ******* 二、加密和解密要用到的方法供参考
1.参数进行排序并连接成 key1value1key2value2 字符串并将sign参数排除在外
func GetSignStr(maps map[string]string) string {signData : make(map[string]string, 0)for k, v : range maps {if k ! sign {signData[k] v}}var dataParams stringvar keys []stringfor k : range signData {keys append(keys, k)}sort.Strings(keys)for _, k : range keys {dataParams dataParams k signData[k] }ff : dataParams[0 : len(dataParams)-1]return ff
}
2.将排序好的参数字符串进行证书私钥加密并将加密串base64转换
// 将排序好的参数进行加密签名并转成base64字符串返回 signData参数字符串 pemPath 私钥路径./aaa/bbb/private.pem
func PrivateSha1SignData(signData, pemPath string) (string, error) {h : crypto.Hash.New(crypto.SHA1)h.Write([]byte(signData))hashed : h.Sum(nil)_, private, err : LoadPrivateKey(pemPath)if err ! nil {return , err}signer, err : rsa.SignPKCS1v15(rand.Reader, private, crypto.SHA1, hashed)if err ! nil {fmt.Println(PrivateSha1SignData Error from signing: %s\n, err)return , err}return Base64Encode(signer), nil
}// 根据路径加载证书文件
func LoadPrivateKey(pemPath string) (string, *rsa.PrivateKey, error) {key, err : ioutil.ReadFile(pemPath)if err ! nil {return , nil, err}block, _ : pem.Decode(key)if block nil {return , nil, errors.New(pem.Decode err)}p, err : x509.ParsePKCS8PrivateKey(block.Bytes)if err ! nil {return , nil, err} else {pk : p.(*rsa.PrivateKey)return , pk, nil}
}// base64 加密
func Base64Encode(data []byte) string {return base64.StdEncoding.EncodeToString(data)
}// base64 解密
func Base64Decode(data string) ([]byte, error) {return base64.StdEncoding.DecodeString(data)
}
3.将sign和加密参数转成map post方式请求第三方接口而此处需要注意接口返回的参数返回的参数sign值中是带有 和 / 符号的按正常情况 用 url.QueryUnescape(string(body[:]))转换是安全的但奇怪的是本人遇到过用此方法反而字符串中出现了空格所以用string(body[:]就OK了
func PayPost(requrl string, request map[string]string, publicPemPath string) (respData string, err error) {http : TimeoutClient()resp, err : http.Post(requrl, application/x-www-form-urlencoded, strings.NewReader(HttpBuildQuery(request)))if err ! nil {return respData, errors.New(paypost err1: err.Error())}if resp.StatusCode ! 200 {return respData, fmt.Errorf(http request response StatusCode:%v, resp.StatusCode)}defer resp.Body.Close()data, err : ioutil.ReadAll(resp.Body)if err ! nil {return respData, errors.New(paypost err2: err.Error())}dataString : string(data[:])//dataString, err : url.QueryUnescape(string(data[:]))//if err ! nil {// return respData, errors.New(paypost err3: err.Error())//}return respData, err
}
4. 对方回调我们方接口接收参数
//获取参数字节码body, err : ioutil.ReadAll(c.Request.Body)info : if err ! nil {//返回错误return}//参数转成字符串dataString, err : url.QueryUnescape(string(body[:]))if err ! nil {//返回错误return}
5.解密
// signStr参数字符kvkv
// signature:加密串base64需要转换
// PublicPemPath第三方公约路径 ./aaa/bbb/public.pem
// 解密
func YSCallbackVerify(signStr,signature, PublicPemPath string) (bool, error) {hash : crypto.Hash.New(crypto.SHA1)hash.Write([]byte(str))hashed : hash.Sum(nil)var inSign []byteinSign, err : Base64Decode(signature)if err ! nil {return false, errors.New(解析signature失败: err.Error())}publicPem, err : LoadPublicKey(PublicPemPath)if err ! nil {return false, errors.New(获取公钥失败 err.Error())}err rsa.VerifyPKCS1v15(publicPem, crypto.SHA1, hashed, inSign)if err ! nil {return false, errors.New(PublicSha1Verify Error from signing: err.Error())}return true, nil
}//加载公约证书
func LoadPublicKey(pemPath string) (*rsa.PublicKey, error) {key, err : ioutil.ReadFile(pemPath)if err ! nil {return nil, errors.New(加载公钥错误1 err.Error())}block, _ : pem.Decode(key)if block nil {return nil, errors.New(加载公钥错误2 err.Error())}certBody, err : x509.ParseCertificate(block.Bytes)if err ! nil {return nil, errors.New(加载公钥错误3 err.Error())}pb : certBody.PublicKey.(*rsa.PublicKey)return pb, nil
}
6.说明请求第三方接口一般参数会有两层或更多层那么我们获取到参数而第二层原本是json字符串那么就需要注意我们golang定义的结构体struct各字段就需要跟它对应以免参数书序不对造成解密失败。当然如上方法都是用的map接收参数当中需要将struct转成map 将map转成struct则需要根据需要来灵活转换。