nodejs 如何通过API 证书(权威CA颁发)下载敏感信息加密公钥证书?原理解析 您所在的位置:网站首页 证书加密原理图 nodejs 如何通过API 证书(权威CA颁发)下载敏感信息加密公钥证书?原理解析

nodejs 如何通过API 证书(权威CA颁发)下载敏感信息加密公钥证书?原理解析

2022-08-31 01:15| 来源: 网络整理| 查看: 265

非必填字段的值如果为空,请求报文里面不能传递该参数,否则会报错 微信支付侧有可能在不破坏协议兼容性的前提下,增加请求参数或者应答对象中的字段。商户应当兼容未来可能加入的新字段。 认证方式: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("" + err.message + "")} }); rq.end(); });


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有