OpenSDK签名开发手册
# OpenSDK分享签名开发手册
# 签名算法
开发者在配置公钥或证书时会指定签名算法。平台支持如下两种签名算法,可以在管理中心-应用详情-API安全功能中进行配置,详见安全鉴权模式介绍。
- RSAwithSHA256
- SM2withSM3´
# 签名生成
签名串生成规则 Key1=Base64(Value1)&Key2=Base64(Value2)...
其中Key为各待签名字段,Value为待签名数据。Key保留大小写,所有字段按Key的字典序排序(根据ASCII码值从小到大排序)。Value需进行标准Base64编码(无换行符\n
)。若Value内包含Unicode字符,需先进行UTF-8编码。
签名串生成后,使用配置的签名算法进行签名。
若算法为RSAwithSHA256,需使用PSS填充方式,指定salt_length为32。(PSS签名中包含随机因子,因此每次签名结果都不同,只能验签)
# 分享接口字段
OpenSDK涉及的分享接口可参考开发手册
涉及图片展示的分享接口,对应的消息对象中新增了imgDataHash
、thumbDataHash
、hdAlbumThumbFileHash
等图片哈希字段,需要开发者计算图片对应的哈希值。
开发者生成消息签名后,需填入消息对象的 msgSignature
字段。
thumbDataHash
等图片哈希字段与msgSignature
字段增加在WXMediaMessage,与thumbData
同级,其他新增字段增加在对应消息对象里。
# 各消息对象待签名字段
文字类型分享
WXTextObject
图片类型分享
WXImageObject
音乐类型分享
WXMusicObject
(废弃)视频类型分享
WXVideoObject
网页类型分享
WXWebpageObject
小程序类型分享
WXMiniProgramObject
音乐视频类型分享
WXMusicVideoObject
# 示例
以网页类型分享为例,对分享数据签名。
# RSAwithSHA256
签名使用PSS填充方式,需要指定salt长度为32。(PSS签名中包含随机因子,因此每次签名结果都会变化)
私钥信息
{
"Appid": "wxba6223c06417af7b",
"Sn": "97845f6ed842ea860df6fdf65941ff56",
"PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA3FoQOmOl5/CF5hF7ta4EzCy2LaU3Eu2k9DBwQ73J82I53Sx9\nLAgM1DH3IsYohRRx/BESfbdDI2powvr6QYKVIC+4Yavwg7gzhZRxWWmT1HruEADC\nZAgkUCu+9Il/9FPuitPSoIpBd07NqdkkRe82NBOfrKTdhge/5zd457fl7J81Q5VT\nIxO8vvq7FSw7k6Jtv+eOjR6SZOWbbUO7f9r4UuUkXmvdGv21qiqtaO1EMw4tUCEL\nzY73M7NpCH3RorlommYX3P6q0VrkDHrCE0/QMhmHsF+46E+IRcJ3wtEj3p/mO1Vo\nCpEhawC1U728ZUTwWNEii8hPEhcNAZTKaQMaTQIDAQABAoIBAQCXv5p/a5KcyYKc\n75tfgekh5wTLKIVmDqzT0evuauyCJTouO+4z/ZNAKuzEUO0kwPDCo8s1MpkU8boV\n1Ru1M8WZNePnt65aN+ebbaAl8FRzNvltoeg9VXIUmBvYcjzhOVAE4V2jW7M8A9QU\nzUpyswuED6OeFKfOHtYk2In2IipAqhfbyc6gn7uZSWTQsoO6hGBRQ7Ejx+vgwrbx\nZKVZ7UXbPHD0lOEPraA3PH/QUeUKpNwK2NXQoBxWcR283/HxFSAjjSSsGSBKsCnw\nDN55P2FQ0HNi5YrwUNT9190NIXSeygaRy1b+D+yBfm+yE7/qXwHLZCHsjO+2tMSS\n3KGjllTBAoGBAP9FPeYNKZuu5jt9RpZwXCc9E7Iz7bmM7zws6dun6dQH0xVVWFVm\niGIu07eqyB8HNagXseFzoXLV5EQx+3DaB0bAH+ZEpHGJJpAWSLusigssFUFuTvTF\nw+rC5hxOfidMa6+93SU5pWeJb0zJF8PRDaJ3UmwlwpYubF17sT4PD6p9AoGBANz7\nRlhRSFvggJjhEMpek3OIYWrrlRNO2MVcP7i/fGNTHhrw7OHcNGRof54QZ2Y0baL7\n1vHNokbK2mnT+cQXY/gXMmcE/eV4xyRGYiIL9nBdrkLerc43EYPv+evDvgyji6+y\n4np5cKqHrS8F+YzATk82Jt9HgdI2MvfbJTkSbmgRAoGAHNPL9rPb1An/VA6Ery6H\nKaM7Gy/EE+U3ixsjWbvvqxMrIkieDh7jHftdy2sM6Hwe8hmi6+vr+pTvD0h5tbfZ\nhILj11Q/Idc0NKdflVoZyMM0r0vuvLOsuVFDPUUb+AIoUxNk6vREmpmpqQk4ltN/\n763779yfyef6MuBqFrEKut0CgYB9FfsuuOv1nfINF7EybDCZAETsiee7ozEPHnWv\ndSzK6FytMV1VSBmcEI7UgUKWVu0MifOUsiq+WcsihmvmNLtQzoioSeoSP7ix7ulT\njmP0HQMsNPI7PW67uVZFv2pPqy/Bx8dtPlqpHN3KNV6Z7q0lJ2j/kHGK9UUKidDb\nKnS2kQKBgHZ0cYzwh9YnmfXx9mimF57aQQ8aFc9yaeD5/3G2+a/FZcHtYzUdHQ7P\nPS35blD17/NnhunHhuqakbgarH/LIFMHITCVuGQT4xS34kFVjFVhiT3cHfWyBbJ6\nGbQuzzFxz/UKDDKf3/ON41k8UP20Gdvmv/+c6qQjKPayME81elus\n-----END RSA PRIVATE KEY-----",
"PublicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FoQOmOl5/CF5hF7ta4E\nzCy2LaU3Eu2k9DBwQ73J82I53Sx9LAgM1DH3IsYohRRx/BESfbdDI2powvr6QYKV\nIC+4Yavwg7gzhZRxWWmT1HruEADCZAgkUCu+9Il/9FPuitPSoIpBd07NqdkkRe82\nNBOfrKTdhge/5zd457fl7J81Q5VTIxO8vvq7FSw7k6Jtv+eOjR6SZOWbbUO7f9r4\nUuUkXmvdGv21qiqtaO1EMw4tUCELzY73M7NpCH3RorlommYX3P6q0VrkDHrCE0/Q\nMhmHsF+46E+IRcJ3wtEj3p/mO1VoCpEhawC1U728ZUTwWNEii8hPEhcNAZTKaQMa\nTQIDAQAB\n-----END PUBLIC KEY-----"
}
原始分享数据
{
"title": "OpenSDK分享测试",
"description": "APP分享到微信内。",
"webpageUrl": "https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Resource_Center_Homepage.html",
"thumbData": "\u0089PNG\r\n\u001a..." // 缩略图字节流,原图于下方下载
}
示例缩略图下载
计算缩略图哈希值
thumbDataHash = 12c593c5bd2f0972188eb30e32bee7ad4060be582c2c6826caff89372ca84707
拼接签名串
按字典序对待签名字段排序
appid
description
thumbDataHash
title
webpageUrl
对字段内容进行标准base64编码,若存在中文等Unicode字符,需先进行UTF-8编码
appid = d3hiYTYyMjNjMDY0MTdhZjdi // wxba6223c06417af7b
description = QVBQ5YiG5Lqr5Yiw5b6u5L+h5YaF44CC // APP分享到微信内。
thumbDataHash = MTJjNTkzYzViZDJmMDk3MjE4OGViMzBlMzJiZWU3YWQ0MDYwYmU1ODJjMmM2ODI2Y2FmZjg5MzcyY2E4NDcwNw== // 12c593c5bd2f0972188eb30e32bee7ad4060be582c2c6826caff89372ca84707
title = T3BlblNES+WIhuS6q+a1i+ivlQ== // OpenSDK分享测试
webpageUrl = aHR0cHM6Ly9kZXZlbG9wZXJzLndlaXhpbi5xcS5jb20vZG9jL29wbGF0Zm9ybS9Nb2JpbGVfQXBwL1Jlc291cmNlX0NlbnRlcl9Ib21lcGFnZS5odG1s // https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Resource_Center_Homepage.html
拼接各个字段(末尾无换行符,SHA256为5d39dda9503893e4962a7590effc85f444e5a48aa71d25e44cafab8350003bab
)
appid=d3hiYTYyMjNjMDY0MTdhZjdi&description=QVBQ5YiG5Lqr5Yiw5b6u5L+h5YaF44CC&thumbDataHash=MTJjNTkzYzViZDJmMDk3MjE4OGViMzBlMzJiZWU3YWQ0MDYwYmU1ODJjMmM2ODI2Y2FmZjg5MzcyY2E4NDcwNw==&title=T3BlblNES+WIhuS6q+a1i+ivlQ==&webpageUrl=aHR0cHM6Ly9kZXZlbG9wZXJzLndlaXhpbi5xcS5jb20vZG9jL29wbGF0Zm9ybS9Nb2JpbGVfQXBwL1Jlc291cmNlX0NlbnRlcl9Ib21lcGFnZS5odG1s
计算签名
使用PSS填充方式计算签名S
(内含随机因子每次结果都不同,需使用公钥验证签名)
base64_encode(S) = xQ/bmfmPMXRGyZvGm9sNFHcOjd5CVG9t0/qfZVOQPiHBR/cvalore/eI3PH+mzF/pxhF9JT3pev+NsT/tS8tXf2XGxE8I+b0f5TeG3hXWD1O8xizG4ZS+5ht8RjdwsyxFSriLJnsbS+oA+2E2XLDFjdSjN0JGB9/9Rfa5J6FwxE2pMbxth0EgatDj3Io7MMbKdZExK6GLaqZiWKDNOXk4RPPdULiIqh71sc3uiC955N10CQHyVclOiYSrAcf6Fr//baEI/IzPWeUUghj8uS6ljGCROpLkc9xiEwEd67VwoIWSyzGilNLkvgYh9Gn9iiiqXMTxNiVScSZE32DnJKjVg==
最终分享数据
{
"title": "OpenSDK分享测试",
"description": "APP分享到微信内。",
"webpageUrl": "https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Resource_Center_Homepage.html",
"thumbData": "\u0089PNG\r\n\u001a...",
"thumbDataHash": "12c593c5bd2f0972188eb30e32bee7ad4060be582c2c6826caff89372ca84707",
"msgSignature": "xQ/bmfmPMXRGyZvGm9sNFHcOjd5CVG9t0/qfZVOQPiHBR/cvalore/eI3PH+mzF/pxhF9JT3pev+NsT/tS8tXf2XGxE8I+b0f5TeG3hXWD1O8xizG4ZS+5ht8RjdwsyxFSriLJnsbS+oA+2E2XLDFjdSjN0JGB9/9Rfa5J6FwxE2pMbxth0EgatDj3Io7MMbKdZExK6GLaqZiWKDNOXk4RPPdULiIqh71sc3uiC955N10CQHyVclOiYSrAcf6Fr//baEI/IzPWeUUghj8uS6ljGCROpLkc9xiEwEd67VwoIWSyzGilNLkvgYh9Gn9iiiqXMTxNiVScSZE32DnJKjVg=="
}
示例代码
Node.js
// RSAwithSHA256
const crypto = require("crypto")
// 仅做演示,敏感信息请勿硬编码
function getCtx() {
let ctx = {
local_private_key: "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA3FoQOmOl5/CF5hF7ta4EzCy2LaU3Eu2k9DBwQ73J82I53Sx9\nLAgM1DH3IsYohRRx/BESfbdDI2powvr6QYKVIC+4Yavwg7gzhZRxWWmT1HruEADC\nZAgkUCu+9Il/9FPuitPSoIpBd07NqdkkRe82NBOfrKTdhge/5zd457fl7J81Q5VT\nIxO8vvq7FSw7k6Jtv+eOjR6SZOWbbUO7f9r4UuUkXmvdGv21qiqtaO1EMw4tUCEL\nzY73M7NpCH3RorlommYX3P6q0VrkDHrCE0/QMhmHsF+46E+IRcJ3wtEj3p/mO1Vo\nCpEhawC1U728ZUTwWNEii8hPEhcNAZTKaQMaTQIDAQABAoIBAQCXv5p/a5KcyYKc\n75tfgekh5wTLKIVmDqzT0evuauyCJTouO+4z/ZNAKuzEUO0kwPDCo8s1MpkU8boV\n1Ru1M8WZNePnt65aN+ebbaAl8FRzNvltoeg9VXIUmBvYcjzhOVAE4V2jW7M8A9QU\nzUpyswuED6OeFKfOHtYk2In2IipAqhfbyc6gn7uZSWTQsoO6hGBRQ7Ejx+vgwrbx\nZKVZ7UXbPHD0lOEPraA3PH/QUeUKpNwK2NXQoBxWcR283/HxFSAjjSSsGSBKsCnw\nDN55P2FQ0HNi5YrwUNT9190NIXSeygaRy1b+D+yBfm+yE7/qXwHLZCHsjO+2tMSS\n3KGjllTBAoGBAP9FPeYNKZuu5jt9RpZwXCc9E7Iz7bmM7zws6dun6dQH0xVVWFVm\niGIu07eqyB8HNagXseFzoXLV5EQx+3DaB0bAH+ZEpHGJJpAWSLusigssFUFuTvTF\nw+rC5hxOfidMa6+93SU5pWeJb0zJF8PRDaJ3UmwlwpYubF17sT4PD6p9AoGBANz7\nRlhRSFvggJjhEMpek3OIYWrrlRNO2MVcP7i/fGNTHhrw7OHcNGRof54QZ2Y0baL7\n1vHNokbK2mnT+cQXY/gXMmcE/eV4xyRGYiIL9nBdrkLerc43EYPv+evDvgyji6+y\n4np5cKqHrS8F+YzATk82Jt9HgdI2MvfbJTkSbmgRAoGAHNPL9rPb1An/VA6Ery6H\nKaM7Gy/EE+U3ixsjWbvvqxMrIkieDh7jHftdy2sM6Hwe8hmi6+vr+pTvD0h5tbfZ\nhILj11Q/Idc0NKdflVoZyMM0r0vuvLOsuVFDPUUb+AIoUxNk6vREmpmpqQk4ltN/\n763779yfyef6MuBqFrEKut0CgYB9FfsuuOv1nfINF7EybDCZAETsiee7ozEPHnWv\ndSzK6FytMV1VSBmcEI7UgUKWVu0MifOUsiq+WcsihmvmNLtQzoioSeoSP7ix7ulT\njmP0HQMsNPI7PW67uVZFv2pPqy/Bx8dtPlqpHN3KNV6Z7q0lJ2j/kHGK9UUKidDb\nKnS2kQKBgHZ0cYzwh9YnmfXx9mimF57aQQ8aFc9yaeD5/3G2+a/FZcHtYzUdHQ7P\nPS35blD17/NnhunHhuqakbgarH/LIFMHITCVuGQT4xS34kFVjFVhiT3cHfWyBbJ6\nGbQuzzFxz/UKDDKf3/ON41k8UP20Gdvmv/+c6qQjKPayME81elus\n-----END RSA PRIVATE KEY-----",
local_sn: "97845f6ed842ea860df6fdf65941ff56"
}
return ctx
}
function getReq() {
let req = {
share_type: "WXWebpageObject",
appid: "wxba6223c06417af7b",
title: "OpenSDK分享测试",
description: "APP分享到微信内。",
webpageUrl: "https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Resource_Center_Homepage.html",
thumbDataHash: "12c593c5bd2f0972188eb30e32bee7ad4060be582c2c6826caff89372ca84707"
}
return req
}
function getShareParams(share_type) {
if (typeof share_type !== "string") return undefined
let share_params = {
"WXTextObject": ["appid", "text"],
"WXImageObject": ["appid", "imgDataHash"],
"WXVideoObject": ["appid", "title", "description", "thumbDataHash", "videoUrl", "videoLowBandUrl"],
"WXWebpageObject": ["appid", "title", "description", "thumbDataHash", "webpageUrl"],
// ... 按需配置参数列表
}
return share_params[share_type]
}
function getSignature(ctx, req) {
const { local_private_key } = ctx // 开发者本地信息
const share_type = req["share_type"]
const param_list = getShareParams(share_type)
param_list.sort() // 确保参数列表按字典序排序
let payload = ""
for (let i = 0; i < param_list.length; i += 1) {
const param = param_list[i]
let value = req[param]
if (typeof value !== "string") value = ""
let new_value = Buffer.from(value, "utf-8").toString("base64")
if (i > 0) payload += "&"
payload += `${param}=${new_value}`
}
// console.log(payload)
const data_buffer = Buffer.from(payload, 'utf-8')
const key_obj = {
key: local_private_key,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST // salt长度,需与SHA256结果长度(32)一致
}
const sig_buffer = ss_buffer = crypto.sign(
'RSA-SHA256',
data_buffer,
key_obj
)
const sig = sig_buffer.toString('base64')
return sig
}
const ctx = getCtx()
const req = getReq()
let res = getSignature(ctx, req)
console.log(res)