1.UserSig 简介
腾讯云 IM 的前身是 QQ 的即时通讯消息系统,我们将 QQ 的消息模块进行了抽离,变成了适合移动端接入的 IM SDK。将消息后台进行改造,使其脱离对 QQ 号码的绑定,构成了现在的 IM 云通讯后台。
QQ 可以用来收发单聊和群聊的消息,但前提是您必须先登录才能使用。登录 QQ 用的是 QQ 号和密码,登录 IM SDK 也是一样,只是肯定不能再用 QQ 号和密码了,而是使用您指定的用户名(userid)和密码(usersig)。
用户名(userid)
可以是您目前 App 里的用户 ID,比如您有一个用户,他/她的账号 ID 是 27149, 那么您就可以用 27149 作为登录 IM SDK 的 userid。
密码(usersig)
既然您指定了 27149 是您的用户,腾讯云如何才能确认该用户是您认可的合法用户呢?usersig 就是用于解决这个问题的,usersig 本质是对 userid、appid 等信息的
非对称加密非对称加密用的加密密钥和解密密钥是不同的,您的服务器可以持有私钥并对 userid 和 appid 进行非对称加密,加密之后的结果就是 usersig ;而腾讯云同步持有您的公钥,这样一来,腾讯云就可以确认 usersig 是否合法,从而可以确认它是否是由您的服务器签发的
2.生成 UserSig
官网的 SDK 下载区提供了一份计算 UserSig 的简单版源码,有 Java 和 PHP 两个版本的:https://github.com/TencentVideoCloudMLVBDev/usersig_server_source
下面的代码是用Python生成UserSig的方法,用的sanic框架,主要参考:http://bbs.qcloud.com/thread-14366-1-1.html
from sanic.views import HTTPMethodView
from sanic import response
from sanic import Blueprint
import OpenSSL
import base64
import zlib
import json
import time
UserSigList_Blueprint = Blueprint(__name__,url_prefix='/api/banpai/v1.2/')
#ec格式的私钥
# ecdsa_pri_key ="""
# -----BEGIN EC PRIVATE KEY-----
# MHcCAQEEIP+EunCa1keJ/ymk+MXhyb22+SHGgtdbYmdsBRzkWhohoAoGCCqGSM49
# AwEHoUQDQgAEXFBfDBCbwt0ZjBUMP9jJ/iqAcPeDi1UVzh51GuhGfNkRezQBTK/C
# 4rgi6oH24Asxeo1jnhFywp13qxFVYiKhtA==
# -----END EC PRIVATE KEY-----
# """
ecdsa_pri_key ="""
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIKLXR3F2OJkMrrzJtzhH52OnK+rVoUA4pcns3XdYoSfDoAoGCCqGSM49
AwEHoUQDQgAEbRGCpFwUNOjIMUHy7lcF3rDqZ0KvJpoua3VAiP3+5kjlYFHzLneg
0olpV4eJDb3RhGZ3bANgoOZdRxtlI1TR/Q==
-----END EC PRIVATE KEY-----
"""
# sdk_appid =
sdk_appid = 注册官方账号会提供
def list_all_curves():
list = OpenSSL.crypto.get_elliptic_curves()
for element in list:
print(element)
def get_secp256k1():
print(OpenSSL.crypto.get_elliptic_curve('secp256k1'))
def base64_encode_url(data):
base64_data = base64.b64encode(data)
base64_data = base64_data.replace(b'+', b'*')
base64_data = base64_data.replace(b'/', b'-')
base64_data = base64_data.replace(b'=', b'_')
return base64_data
def base64_decode_url(base64_data):
base64_data = base64_data.replace(b'*', b'+')
base64_data = base64_data.replace(b'-', b'/')
base64_data = base64_data.replace(b'_', b'=')
raw_data = base64.b64decode(base64_data)
return raw_data
class TLSSigAPI:
""""""
__acctype = 0
__identifier = ""
__appid3rd = ""
__sdkappid = 0
__version = 20151204
__expire = 3600 * 24 * 30 # 默认一个月,需要调整请自行修改
__pri_key = ""
__pub_key = ""
_err_msg = "ok"
def __get_pri_key(self):
return OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, self.__pri_key)
def __init__(self, sdkappid, pri_key):
self.__sdkappid = sdkappid
self.__pri_key = pri_key
def __create_dict(self):
return {"TLS.account_type": "%d" % self.__acctype, "TLS.identifier": "%s" % self.__identifier,
"TLS.appid_at_3rd": "%s" % self.__appid3rd, "TLS.sdk_appid": "%d" % self.__sdkappid,
"TLS.expire_after": "%d" % self.__expire, "TLS.version": "%d" % self.__version,
"TLS.time": "%d" % time.time()}
def __encode_to_fix_str(self, m):
fix_str = "TLS.appid_at_3rd:" + m["TLS.appid_at_3rd"] + "\n" \
+ "TLS.account_type:" + m["TLS.account_type"] + "\n" \
+ "TLS.identifier:" + m["TLS.identifier"] + "\n" \
+ "TLS.sdk_appid:" + m["TLS.sdk_appid"] + "\n" \
+ "TLS.time:" + m["TLS.time"] + "\n" \
+ "TLS.expire_after:" + m["TLS.expire_after"] + "\n"
return fix_str
def tls_gen_sig(self, identifier):
self.__identifier = identifier
m = self.__create_dict()
fix_str = self.__encode_to_fix_str(m)
pk_loaded = self.__get_pri_key()
sig_field = OpenSSL.crypto.sign(pk_loaded, fix_str, "sha256")
sig_field_base64 = base64.b64encode(sig_field)
m["TLS.sig"] = sig_field_base64.decode('utf-8')
json_str = json.dumps(m)
sig_compressed = zlib.compress(json_str.encode('utf-8'))
base64_sig = base64_encode_url(sig_compressed)
return base64_sig
下面用文字简要描述下,
1.将用户的信息组装成一个字符串(json格式的,是直接拼装的,因为顺序不能乱),是哪些信息,可以看 __encode_to_fix_str;
2.使用 sha256 将字符串 hash,然后再用私钥签名,一般加密接口都会一把搞定,加密曲线使用的是 secp256k1;
3.把第2步得到的缓冲区进行base64;
4.将所有用户的信息以及第3步得到签名写进一个 json 串,此时可以不论顺序;
5.将 json 进行序列化,再 zlib 压缩,最后 base64(替换了某些字符,具体哪些看代码),出炉。
特别注意,这段代码在 windows 上验证没问题,但是红帽系(rel 和 centos)上不支持我们使用的曲线,list_all_curves 可以打印所有支持的曲线,红帽系的没有secp256k1。
如果发现系统所带的 openssl 扩展不支持我们选定的曲线,可以参考 http://bbs.qcloud.com/thread-23280-1-1.html。