golang 对接第三方接口 RSA 做签(加密) 验签(解密)

news2024/12/28 23:53:14

一、过程

1.调用第三方接口前,一般需要按规则将参数按key1=value1&key2=value2 阿斯克码排序,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.crt
  openssl pkcs12 -in xxxx.pfx -clcerts -nokeys -out key.cert
  openssl x509 -inform der -in xxx.cer -out xxx.pem    #公钥pem *******

二、加密和解密要用到的方法(供参考)

1.参数进行排序并连接成 key1=value1&key2=value2 字符串并将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 string
	var keys []string
	for 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:参数字符k=v&k=v
// 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 []byte
	inSign, 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则需要根据需要来灵活转换。

  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1541077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue iframe实现父页面实时调用子页面方法和内容,已解决

父页面标签添加鼠标按下事件 父页方法中建立iframe通信 实时调用子页面方法 实时更改子页面文本内容

Chrome 114 带着侧边栏扩展来了

效果展示 manifest.json {"manifest_version": 3,"name": "ChatGPT学习","version": "0.0.2","description": "ChatGPT,GPT-4,Claude3,Midjourney,Stable Diffusion,AI,人工智能,AI","icons"…

C语言经典算法-9

文章目录 其他经典例题跳转链接46.稀疏矩阵47.多维矩阵转一维矩阵48.上三角、下三角、对称矩阵49.奇数魔方阵50.4N 魔方阵51.2(2N1) 魔方阵 其他经典例题跳转链接 C语言经典算法-1 1.汉若塔 2. 费式数列 3. 巴斯卡三角形 4. 三色棋 5. 老鼠走迷官(一)6.…

【go从入门到精通】for循环控制

作者简介: 高科,先后在 IBM PlatformComputing从事网格计算,淘米网,网易从事游戏服务器开发,拥有丰富的C,go等语言开发经验,mysql,mongo,redis等数据库,设计模…

ssm003在线医疗服务系统+jsp

在线医疗服务系统的设计与实现 摘 要 网络技术和计算机技术发展至今,已经拥有了深厚的理论基础,并在现实中进行了充分运用,尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代,所以对于信息的宣传和管…

bevformer转模型经验(需要时序tranformer所有模型都可以参考)

先上bevformer的网络结构图 不难发现,他有两个输入。当前的环视图和历史bev。历史bev是通过历史环视图生成的。也就是说在生成bev特征提取模型这部分被使用了两次。在装模型时候,需要作以下工作: 1 bev特征提取模型单独提出来,转…

用Kimichat学习王庆法老师关于Sora的文章

目录 一 引言:二 提示词方面:三 与Kimi的聊天记录我:假如你是一名大模型方面的专家,提取一下这篇文章的核心观点,用三列表格的形式,https://mp.weixin.qq.com/s/Y-vmxmPu4_-tHaeP35hDJg我:上述文章的一、Spacetime Latent Patches 潜变量时空碎片, 建构视觉语言系统部分…

python处理Excel的方法之xlrd

python处理Excel常用到的模块是xlrd。使用xlrd可以非常方便的处理Excel文档,下面介绍一下基本用法 打开文件 import xlrd data xlrd.open_workbook("c:\\skills.xls") 获取一个工作表 table data.sheet_by_name(uskills) #也可以 table data.sheet_by_…

PTA-练习7

目录 实验8-2-1 找最小的字符串 实验8-2-2 找最长的字符串 实验8-2-8 字符串排序 实验8-2-10 IP地址转换 结构体 实验9-1 计算职工工资 实验9-2 时间换算 实验9-3 计算平均成绩 实验9-5 查找书籍 实验9-8 通讯录排序 实验9-9 有理数比较 实验11-1-1 英文单词排序 …

在 Windows 中安装配置并启动运行 Jenkins【图文详细教程】

安装 Jenkins 的系统要求: 最少 256MB 可用内存最少 1GB 可用磁盘空间JDK 8 / 11 /17(Jenkins 是用 Java 写的,打包成 war 包) 查看 JDK 的版本 Java JDK 在 Windows 中安装可以参考:https://www.yuque.com/u27599042/…

iscsi网络协议(连接硬件设备)

iscsi概念 iscsi是一种互联网协议,用于将存储设备(如硬盘驱动器或磁带驱动器)通过网络连接到计算机。它是一种存储区域网络(SAN)技术,允许服务器通过网络连接到存储设备,就像它们是本地设备一样…

【C++】1596. 火柴棒三角形问题

问题:1596. 火柴棒三角形问题 类型:基本运算、整数运算 题目描述: 如下图所示,摆 1 个火柴棒三角形需要 3 根火柴,那么摆 2 个火柴棒三角形就需要 6 根火柴,请问摆 n 个火柴棒三角形需要多少根火柴&#…

由浅入深一步步了解什么是哈希(概念向)

文章目录 什么是哈希哈希函数直接定址法除留余数法 哈希冲突闭散列线性探测法二次探测法负载因子和闭散列的扩容 开散列开散列的扩容 非整形关键码 什么是哈希 我们来重新认识一下数据查找的过程: 在顺序结构以及平衡树中,记录的关键码与其存储位置之间…

基因在各个细胞系表达情况

从CCLE下载数据得到基因在每个细胞系中的 现在从DepMap: The Cancer Dependency Map Project at Broad Institute 需要先选择Custom Downloads 就可以下载数据进行处理了&#xff1a; rm(list ls()) library(tidyverse) library(ggpubr) rt <- data.table::fread("…

LeetCode每日一题——统计桌面上的不同数字

统计桌面上的不同数字OJ链接&#xff1a;2549. 统计桌面上的不同数字 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 思路&#xff1a; 这是一个很简单的数学问题&#xff1a; 当n 5时&#xff0c;因为n % 4 1&#xff0c;所以下一天4一定会被放上桌面 当n 4…

SpringBoot 3整合Elasticsearch 8

这里写自定义目录标题 版本说明spring boot POM依赖application.yml配置新建模型映射Repository简单测试完整项目文件目录结构windows下elasticsearch安装配置 版本说明 官网说明 本文使用最新的版本 springboot: 3.2.3 spring-data elasticsearch: 5.2.3 elasticsearch: 8.1…

Unity Mesh简化为Cube mesh

Mesh简化为Cube mesh &#x1f373;食用&#x1f959;子物体独立生成CubeMesh&#x1f96a;合并成一个CubeMesh&#x1f32d;Demo &#x1f373;食用 下载并导入插件&#x1f448;即可在代码中调用。 &#x1f959;子物体独立生成CubeMesh gameObject.ToCubeMesh_Invidual()…

计算机基础系列 —— 汇编语言

Same hardware can run many different programs(Software) 文中提到的所有实现都可以参考&#xff1a;nand2tetris_sol&#xff0c;但是最好还是自己学习课程实现一遍&#xff0c;理解更深刻。 我们在之前的文章里&#xff0c;构建了 Register、RAM 和 ALU&#xff0c;使得我…

前端基础篇-前端工程化 Vue 项目开发流程(环境准备、Element 组件库、Vue 路由、项目打包部署)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 环境准备 1.1 安装 NodeJs 1.2 验证 NodeJs 环境变量 1.3 配置 npm 的全局安装路径 1.4 切换 npm 的淘宝镜像( npm 使用国内淘宝镜像的方法(最新) ) 1.5 查看镜像…

QGIS编译(跨平台编译)056:PDAL编译(Windows、Linux、MacOS环境下编译)

点击查看专栏目录 文章目录 1、PDAL介绍2、PDAL下载3、Windows下编译4、linux下编译5、MacOS下编译1、PDAL介绍 PDAL(Point Data Abstraction Library)是一个开源的地理空间数据处理库,它专注于点云数据的获取、处理和分析。PDAL 提供了丰富的工具和库,用于处理激光扫描仪、…