nodejs 如何通过API 证书(权威CA颁发)下载敏感信息加密公钥证书?
在服务商平台的API接口中,有部分接口在传参时,需要对参数中的敏感信息进行RSA加密(如:小微商户申请入驻、小微商户修改结算信息等)。在这些接口的参数加密说明中,是这样注明的:
加密方法详见敏感信息加密方法说明(该md文件中的变量PUBLIC_KEY_FILENAME是表示平台证书,即为证书及其序列号获取方法说明PDF文档中1.1.5小节中的”加密后的证书内容encrypt_certificate.ciphertext"解密后的明文。) |
因此,我们在对敏感信息加密前,需要先获得ciphertext,然后对ciphertext进行解密,解密出来的明文就是加密敏感信息所需的加密公钥了,将该公钥保存为文件就是公钥证书啦(该公钥证书有效期为5年,不过微信支付要求“中控服务器需要定时查询商户的平台证书列表,查询间隔应小于 12 小时,并及时下载新的平台证书。下载证书时,需与本地证书序列表对比,如果发现有新增证书序列号,那就是需要新换的证书。老证书需要在被弃用前及时清理掉”)
关于获取公钥的具体说明可以参阅腾讯官方提供的pdf文档 《证书及其序列号获取方法说明 》 中的1.1.3.3 以外的章节。
1.1.2. 接口地址
请求 Url |
https://api.mch.weixin.qq.com/v3/certificates |
请求方式 |
GET |
1.1.3. 接口调用规则
- 非必填字段的值如果为空,请求报文里面不能传递该参数,否则会报错
- 微信支付侧有可能在不破坏协议兼容性的前提下,增加请求参数或者应答对象中的字段。商户应当兼容未来可能加入的新字段。
- 认证方式:HTTPS 认证,SHA256 with RSA 签名
- 字符集默认使用 UTF-8,请勿使用其它字符集
- 商户与微信之间的交互(特别是支付通知回调),都需要验证签名
- 处理返回时先判断 HTTP 状态码,再判断返回数据中的错误码,才能确定交易状态
- 返回和提交数据的签名,商户号,时间戳,随机串等在 HTTP 头中传递
- HTTP 请求头设置规则如下:
请求头 |
必填 |
说明 |
Accept |
是 |
应答的格式。目前仅支持:application/json |
Accept-Language |
否 |
应答的区域语言。目前支持:en,zh-CN,zh-HK,zh-TW,不传则默认是:zh-CN 。详细请参考设置错误描述语言章节 |
Authorization |
是 |
含有服务器用于验证商户身份的凭证。详细信息请参考签名生成方法章节 |
Content-Type |
是 |
请求数据(Body)的格式。当请求包含请求数据时必填。目前仅支持:application/json |
User-Agent |
是 |
发起请求的客户端软件的标识信息 |
1.1.3.1. Authorization 的构造方式
微信支付要求请求通过 HTTP Authorization 头来传递签名。Authorization 由认证类型和签名信息两个部分组成。
Authorization: 认证类型 签名信息
具体组成为:
认证类型:目前为 WECHATPAY2-SHA256-RSA2048
签名信息:
商户号 mchid
请求随机串 nonce_str
签名值 signature(详见 1.1.3.2 计算签名值方法)
时间戳 timestamp
商户证书序列号 serial_no(详见 1.1.3.4 获取商户证书序列号方法)
Authorization 头的示例如下:(注意,示例因为排版可能存在换行,实际数据应在一行,mchid 前有一个空格)
Authorization: WECHATPAY2-SHA256-RSA2048 mchid="10000100",nonce_str="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",signature="hDV4aXhMvfZ31NABElvWHWuxYiR7lB1sjzcpldpWul/62o75d90l5oznquE+uVORPESfzBpCdtU6IiL+1Cdy3rG01sKXrWfFnjr4jm/imFxbq8BbVpE+HbrRXkR/jrc6gqSVuIjJfXSMK1yL5G35WgUWzWdAKiV3ELQk/sSYrhnOiulve/xM2bJvYFQDl/dvMazxW930JLm0lv1tEMuHuqcx5WN+1fq3VJ+J9UvwVTjQT8eXmHAzaYxXHEoDyN2T5/AVzZTuzcCt1cFk5Sj/tNUvDMklxy+eF7hOUCFzo98Z42OsdpC3GV02mYOApeNwVB7I5fCB//jerFqf9/VjA==",timestamp="1507709632",serial_no="345D5C1DB746787546E06E6DAD9E5BE987CEDFCF"
1.1.3.2. 计算签名值方法
构造待签名串
在运用具体的签名算法前,商户需要先构造待签名串。
第一步,获取 HTTP 请求的方法(GET,POST,PUT 等)
GET
第二步,获取请求的 URL,并去除域名的部分,如果链接带参数,参数值必须进行 URLencode。示例请求的 URL 为
/v3/certificates
第三步,生成一个请求随机串,算法可开发者自定义(可调用系统随机数生成函数转化成字符串),建议长度不少于 10 位。
kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg
第四步,获取发起请求时的系统当前时间戳,即格林威治时间 1970 年 01 月 01 日 00 时 00 分 00 秒(北京时间 1970 年 01 月 01 日 08 时 00 分 00 秒)起至现在的总秒数,作为请求时间戳。时间戳必须是最新的,如果时间戳比微信支付服务器时间晚 300 秒,微信支付服务器会不认这个请求并报错,请商户保持自身系统的时间准确。
1507709906
第五步,获取提交数据。注:当请求方法为 GET 时,请求报文为空。
第六步,按照如下方法,组成待签名串。待签名串共有五行,每行包括一个参数,行尾以\n 结束,包括最后一行。请注意,\n 为换行符(ASCII 编码值为 0x0A)。
HTTP 请求方法\n
URL\n
请求时间戳\n
请求随机串\n
请求报文\n
按照以上规则,请求报文的待签名串为:
GET
/v3/certificates
1507709906
kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg
请注意,当请求方法为 GET 时请求报文为空,最后一行仅为一个换行符。
因此可以定义签名串变量
String signContent=“GET\n/v3/certificates\n1507709906\nkYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg\n\n”
计算签名值
1) 如上方法,得到签名串变量 signContent
2) 获取商户证书私钥。请参照博文微信支付服务商API 证书(权威CA颁发)是做什么用的?
超级管理员登录商户平台,在“账户中心”->“API 安全”->”API 证书(权威 CA 颁发)”中申请 API 商户证书,申请过程中会获取到私钥证书文件(申请流程详见 1.1.3.3“申请 API 商户证书“),打开私钥文件获取私钥字符(定义变量 string sKey)
3) 设置 APIv3 密钥
4) 很多编程语言支持签名函数,建议商户优先调用该类函数,使用商户证书私钥(sKey)对待签名串(signContent)进行 SHA256 with RSA 签名,并对签名结果进行 Base64 编码得到签名值。(如 java 语言提供了 PKCS8EncodedKeySpec、KeyFactory、Base64、PrivateKey 和 Signature 等类)
。。。。。。
1.1.4. 请求参数
请求示例:
curl -v -X GET "https://api.mch.weixin.qq.com/v3/certificates" WECHATPAY2-SHA256-RSA2048 -H 'Authorization:mchid="10000100",nonce_str="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",signature="hDV4aXhMvfZ31NABElvWHWuxYiR7lB1sjzcpldpWul/62o75d90l5oznquE+uVORPESfzBpCdtU6IiL+1Cdy3rG01sKXrWfFnjr4jm/imFxbq8BbVpE+HbrRXkR/jrc6gqSVuIjJfXSMK1yL5G35WgUWzWdAKiV3ELQk/sSYrhnOiulve/xM2bJvYFQDl/dvMazxW930JLm0lv1tEMuHuqcx5WN+1fq3VJ+J9UvwVTjQT8eXmHAzaYxXHEoDyN2T5/AVzZTuzcCt1cFk5Sj/tNUvDMklxy+eOF7hOUCFzo98Z42OsdpC3GV02mYOApeNwVB7I5fCB//jerFqf9/VjA==",timestamp="1507709632",serial_no="345D5C1DB746787546E06E6DAD9E5BE987CEDFCF"' -H 'Accept-Language:' -d -H 'Content-Type:application/json' -H 'Accept:application/json' -H 'User-Agent: curl/7.54.0'
1.1.5. 返回结果
异常返回:
名称 |
变量名 |
必填 |
类型 |
示例值 |
描述 |
返回状态码 |
code |
是 |
string(32) |
INVALID_REQUEST |
错误码,枚举值见错误码列表 |
返回信息 |
message |
否 |
string(256) |
参数格式校验错误 |
返回信息,如非空,为错误原因 |
正常返回:
名称 |
变量名 |
必填 |
类型 |
示例值 |
描述 |
加密的平台证书序列号 | serial_no | 是 | string(40) |
5157F09EFDC096DE1 5EBE81A47057A7232F1B8E1 |
证书的序列号 |
证书启用时间 | effective_time | 是 | string(32) | 2018-06-08T10:34:56+08:00 | 启用证书的时间,时间格式为?RFC3339。每个平台证书的启用时间是固定的。 |
证书弃用时间 | expire_time | 是 | string(32) | 2018-06-08T10:34:56+08:00 | 弃用证书的时间,时间格式为?RFC3339。更换平台证书前,会提前24 小时修改老证书的弃用时间,接口返回新老两个平台证书。更换完成后,接口会返回最新的平台证书。 |
加密证书的算法 |
encrypt_certificat e.algorithm |
是 | string(32) | AEAD_AES_256_GCM | 加密证书的算法,密钥为APIv3 KEY, 需要登录商户平台设置 |
加密证书的随机串 |
encrypt_certificat e.nonce |
是 | string(12) | 61f9c719728a | 加密证书的随机串 |
关联数据 |
encrypt_certificat e.associated_data |
是 | string(32) | certificate | 固定值: certificate |
加密后的证书内容 |
encrypt_certificat e.ciphertext |
是 | string(344) |
Y1IPF0kyPUySt2tRe+aJ7TK6c w08pqiXPr1g/agxl16AYarlrcsdq 1P8gcJc4iVkQfYouooRJdF4Eo….. |
使用 APIv3 KEY 和上述参数,可以解密出平台证书的明文。证书明文为PEM 格式。(注意:更换证书时会出现 PEM格式中的证书失效时间与接口返回的证书弃用时间不一致的情况) |
举例如下:
{"data":[{"serial_no":"5157F09EFDC096DE15EBE81A47057A7232F1B8E1","effective_time ":"2018-06-08T10:34:56+08:00","expire_time ":"2018-12-08T10:34:56+08:00","encrypt_certificate":{"algorithm":"AEAD_AES_256_GCM","nonce":"61f9c719728a","associated_data":"certificate","ciphertext":"sRvt… "}},{"serial_no":"50062CE505775F070CAB06E697F1BBD1AD4F4D87", //这个证书序列号在小微商户申请入驻接口调用时需要用到"effective_time ":"2018-12-07T10:34:56+08:00","expire_time ":"2020-12-07T10:34:56+08:00","encrypt_certificate":{"algorithm":"AEAD_AES_256_GCM","nonce":"35f9c719727b","associated_data":"certificate","ciphertext":"aBvt… "}}]
}
“加密后的证书内容”的解密算法:
下面详细描述对通知数据进行解密的流程
- 从微信支付商户平台上获取商户的 APIv3密钥,记为“key”。
- 针对“algorithm”中描述的算法(目前为“AEAD_AES_256_GCM”),取得对应的参数“nonce”和“associated_data”。
- 使用“key”、“nonce”和“associated_data”,对数据密文“ciphertext”进行解密,得到平台证书的原文。
- 将原文写入文件,使用该文件对敏感字段进行加密。
注: “AEAD_AES_256_GCM”算法的接口细节,请参考 rfc5116。微信支付使用的密钥“key”长度为 32 个字节,随机串“nonce”长度 12 个字节,“associated_data”长度小于 16 个字节并可能为空。
很多编程语言支持 “AEAD_AES_256_GCM”算法,如 java 语言中的 Cipher、SecretKey、GCMParameterSpec、Base64 等类。
官方说明pdf文档看完了,现在可以来捋一下步骤了:
- 通过证书私钥字符对报文进行SHA256 with RSA签名
- 将签名与商户号、请求随机串、时间戳、商户证书序列号一起,构建Authorization 头
- 往接口地址 https://api.mch.weixin.qq.com/v3/certificates 发送GET请求,请求时HTTP头需要包括Accept、Content-Type、User-Agent、Authorization等
- 对返回值中的“encrypt_certificate.ciphertext”进行 “AEAD_AES_256_GCM”算法解密
- 保存解密所得的明文为敏感信息加密公钥证书
简单点,直接上nodejs代码。
var https = require("https");
var crypto = require('crypto');app.get('/wpayGenMgPkey',function(req,res){ //1、通过证书私钥通过证书私钥字符对报文进行SHA256 with RSA签名var pcert = '-----BEGIN PRIVATE KEY-----\n这里对应新的API 证书(权威CA颁发)中的私钥文件字符串\n-----END PRIVATE KEY-----';var now = parseInt(Date.now() / 1000);var rdm = parseInt(Math.random() * Math.pow(2, 64));var plainText = 'GET\n/v3/certificates\n' + now + '\n' + rdm + '\n\n';var data = new Buffer(plainText,'utf8');var sign = crypto.createSign("RSA-SHA256");sign.update(data);var signStr = sign.sign(pcert, 'base64');var mch_id = "这里对应服务商商户号";//2、将签名与商户号、请求随机串、时间戳、商户证书序列号一起,构建Authorization 头var Auth = 'WECHATPAY2-SHA256-RSA2048 mchid="' + mch_id + '",nonce_str="' + rdm + '",signature="' + signStr + '",timestamp="' + now + '",serial_no="这里对应新的API 证书(权威CA颁发)中的证书序列号"';//3、往接口地址 https://api.mch.weixin.qq.com/v3/certificates 发送GET请求,请求时HTTP头需要包括Accept、Content-Type、User-Agent、Authorization等var opts = {method:'GET',hostname:'api.mch.weixin.qq.com',port:'443',pfx:fs.readFileSync('./cert/这里对应新的API 证书(权威CA颁发)中p12证书文件.pfx'), //直接将.p12改后缀名为.pfx即可,此配置可以不填写passphrase:mch_id,path:"/v3/certificates",host:'api.mch.weixin.qq.com' }var body = '';var rq = https.request(opts,function(rs){rs.on('data',function(data){body += data;})rs.on('end', function(){var cJson = JSON.parse(body);if(cJson.data){//4、对返回值中的“encrypt_certificate.ciphertext”进行 “AEAD_AES_256_GCM”算法解密var nJson = cJson.data[cJson.data.length - 1];var keys = '这里对应APIv3密钥串';//编码设置var clearEncoding = 'binary';//加密方式var algorithm = 'aes-256-gcm';//向量var iv = nJson.encrypt_certificate.nonce;//加密类型 base64/hex...var cipherEncoding = 'hex';//var cipherEncoding = 'base64';var cipherChunks = []; var cdata = nJson.encrypt_certificate.ciphertext;cdata = new Buffer(cdata,'base64').toString('binary');var decipher = crypto.createDecipheriv(algorithm, new Buffer(keys, clearEncoding), new Buffer(iv, clearEncoding));decipher.setAutoPadding(true); decipher.setAAD(new Buffer(nJson.encrypt_certificate.associated_data, clearEncoding))var data = new Buffer(cdata,clearEncoding); var rtn = decipher.update(data, clearEncoding, "utf-8").toString("utf8");//这里加上这一句反倒会报错,不加的话解密出来的内容后面有一部分乱码,需要剔除//rtn += decipher.final("utf-8"); rtn = rtn.split('-----END CERTIFICATE-----')[0] + '-----END CERTIFICATE-----';rtn = rtn.replace(/\n/g,'\\n')//5、保存解密所得的明文为敏感信息加密公钥证书(这里请自己保存)res.send(rtn); }else{res.send(body);}})});//注意:header必须通过setHeader函数写入,不能直接在opts中写rq.setHeader("Authorization",Auth);rq.setHeader("Accept","application/json");rq.setHeader("Accept-Language",'zh-CN');rq.setHeader("Content-Type","application/json");rq.setHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2763.0 Safari/537.36");//请求内容为空rq.write('');rq.on('error',function(err){if(fn){fn("<return_msg>" + err.message + "</return_msg>")}}); rq.end();
});
nodejs 如何通过API 证书(权威CA颁发)下载敏感信息加密公钥证书?相关推荐
- 微信支付服务商API 证书(权威CA颁发)是做什么用的?
微信支付服务商平台于2018年7月份悄然在[账户中心]的[API安全]中增加了一个"API 证书(权威CA颁发)"版块,由于和原来的"API证书"版块命名相似, ...
- Java实现自签名证书,CA颁发证书
产生证书库,并创建CA X500Name 就是你的个人信息了,查查JAVA API 就行了 import java.io.File; import java.io.FileInputStream ...
- php 微信 ca证书出错,升级CA颁发的证书后微信退款、红包等无法使用
前几天申请了新的微信支付商户号,按照流程申请了权威CA颁发的API证书,使用原来一直能正常使用(发红包.退款)的代码,发现新申请的微信支付无法运行退款接口,错误提示:certificate not m ...
- 数字证书、CA、CA证书,傻傻分不清楚?这一篇看懂!
电子合同得到越来越多人的关注,而电子合同的安全性,更是人们关注的焦点.在电子合同领域,我们经常会听到这么3个词:数字证书.CA.CA证书,而且经常傻傻分不清楚它们到底是什么东西. 那么,今天小编就给大 ...
- 密码学专题 证书和CA指令 申请证书|建立CA|CA操作|使用证书|验证证书
Req指令介绍 功能概述和指令格式 req指令一般来说应该是提供给证书申请用户的工具,用来生成证书请求以便交给CA验证和签发证书.但是,OpenSSL的req指令的功能远比这样的要求强大得多,它不仅可 ...
- 数字证书、CA、CA证书、SSL证书、CA锁 简单介绍
1.数字证书是什么? 数字证书不是数字身份证,而是身份认证机构盖在数字身份证上的一个章或印(或者说加在数字身份证上的一个签名).其作用类似于现实生活中司机的驾驶执照或日常生活中的身份证. 2.CA是什 ...
- SSL数字证书之CA根证书、CA中间证书和SSL证书
[前言] 说一下大背景吧,我们的一个后台服务需要部署在一个没法上外网的环境,但是我们的后台服务需要访问七牛云进行对象存储,于是乎,需要一个代理来完成这个访问,我门采用nginx七层来做这个代理,因为七 ...
- PHP开发微信支付小微商户V3版本 图片上传、生成签名、平台证书获取、平台证书编号、敏感信息加密
吐槽一下,看微信支付小微商户的开发文档头都大了,什么是平台证书.什么是商户API证书...... 好了废话不多说下面明确几个名词: 商户API证书:是由权威CA颁发,用于有关微信支付等操作API接口使 ...
- java ssl证书_Java安全教程–创建SSL连接和证书的分步指南
java ssl证书 在有关应用JEE安全性的系列文章中,我们为您提供了另一个有关如何在Java EE应用程序中创建SSL连接和创建证书的详细教程. 如我们之前的文章中所述, 安全套接字层(SSL)/ ...
最新文章
- 51CTO推荐博客、博客之星名单【2014年】
- JavaScript数组去重算法实例
- 2.11 while循环的嵌套以及应用(难)
- java swt.jar_Eclipse中的swt需要的jar包
- 【机器学习】集成学习之梯度提升树GBDT
- WPF 实现 DataGrid/ListView 分页控件
- 2017年Spring发布了30个新的Android库,值得您关注
- Pandas重复数据的查看和去重
- 源码大招:不服来战!撸这些完整项目,你不牛逼都难!
- linux 运行.net 5,.NET 5 将于2020年问世
- 2021沭阳中学高考成绩查询,沭阳建陵中学2020高考喜报!
- 面向对象的编程-类和实例
- c语言加密字母向右移两位,C语言二进制除法用左右移位来表示
- 编程基本功:如果可能,不用if,尽量使用switch
- 红旗Linux 网卡bond,Linux双网卡绑定一个IP的实现
- el-table点击单元格自动聚焦可编辑,且失去焦点即修改成功的实现方法
- 张帅帅学Java之注释
- Excel两行交换及两列交换,快速互换相邻表格数据的方法
- 求急!!谁会写接口测试,拜托了各位大佬,跪求!!我感激不尽,好好报答他
- Android自定义WebView实现Youtube网络视频播放控件