# 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字段名 说明 备注 appid 当前 appid 无需填入消息对象 text 待分享内容 图片类型分享
WXImageObject字段名 说明 备注 appid 当前 appid 无需填入消息对象 imgDataHash 图片 SHA256 新增字段,用小写十六进制串表示 音乐类型分享
WXMusicObject(废弃)视频类型分享
WXVideoObject字段名 说明 备注 appid 当前 appid 无需填入消息对象 title 消息标题 description 消息描述 thumbDataHash 缩略图 SHA256 新增字段,用小写十六进制串表示 videoUrl 视频链接 videoLowBandUrl 供低带宽的环境下使用的视频链接 网页类型分享
WXWebpageObject字段名 说明 备注 appid 当前 appid 无需填入消息对象 title 消息标题 description 消息描述 thumbDataHash 缩略图 SHA256 新增字段,用小写十六进制串表示 webpageUrl html 链接 小程序类型分享
WXMiniProgramObject字段名 说明 备注 appid 当前 appid 无需填入消息对象 title 消息标题 description 消息描述 thumbDataHash 缩略图 SHA256 新增字段,用小写十六进制串表示 userName 小程序的原始 id path 小程序的 path 音乐视频类型分享
WXMusicVideoObject字段名 说明 备注 appid 当前 appid 无需填入消息对象 title 消息标题 description 消息描述 thumbDataHash 缩略图 SHA256 新增字段,用小写十六进制串表示 musicUrl 音频网页的 URL musicDataUrl 音频数据的 URL singerName 歌手名 duration 歌曲时长 hdAlbumThumbFileHash 高清专辑图 SHA256 新增字段,用小写十六进制串表示 albumName 音乐专辑名 musicGenre 音乐流派 issueDate 发行时间 identification 音乐标识符
# 示例
以网页类型分享为例,对分享数据签名。
# 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-----\n...",
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) {
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}`
}
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
}
const sig_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)