App ID鉴权介绍
为了降低第三方应用集成华为云会议难度,并且提升开放接口的安全性,华为云会议开放能力中支持基于App ID的鉴权方式。App ID是一个应用的标识,同一个App ID可以同时在第三方的桌面终端、移动终端、Web应用上使用。
App ID鉴权原理
- 第三方客户端App ID鉴权原理
图1 第三方客户端App ID鉴权流程
鉴权前提:
- 开发者在华为云会议控制台上为自己的应用申请App ID,并获取App ID和App Key。
- 开发者在自己的服务端集成Signature生成算法,请参考“第三方服务集成Signature生成算法”。
- SDK初始化时传入App ID,请参考《客户端SDK参考》中的“初始化”部分。
鉴权过程:
- 第三方客户端将User ID(第三方User ID)发送给第三方服务器。
- 第三方服务器根据App ID、User ID、App Key等,生成鉴权用的签名材料Signature。
- 第三方服务端返回Signature,以及ExpireTime和Nonce值。
- 第三方客户端调用客户端SDK的Login接口,参数为User ID、Signature、ExpireTime和Nonce。
- 客户端SDK向华为云会议服务端发起鉴权。
- 鉴权通过后SDK获取接入Token。该Token对第三方客户端不可见,由SDK负责维护和刷新。
- 第三方服务App ID鉴权原理
图2 第三方服务端App ID鉴权流程
鉴权前提:
- 开发者在华为云会议控制台上为自己的应用申请App ID,并获取App ID和App Key。
- 开发者在自己的服务端集成Signature生成算法,请参考“第三方服务集成Signature生成算法”。
鉴权过程:
- 第三方服务端根据调用的业务接口所需的User ID(第三方User ID),加上App ID、App Key、ExpireTime和Nonce,生成鉴权用的签名材料Signature。
- 第三方服务端调用华为云会议服务端的App ID鉴权接口(REST接口)。
- 华为云会议服务端鉴权通过后,返回接入Token。
- 第三方服务端使用接入Token调用其他业务接口。
1. 接入Token有效期是12~24小时。
2. 生成Signature和调用appAuth接口时可以不带User ID,如果不带User ID默认是创建企业时的创建者(企业Owner)。
3. User ID需要第三方应用保证在同一个企业内是唯一的。如果第三方开发者是SP,同一个应用在多个企业使用,接口中除了带User ID外,还需要带Corp ID(企业ID)。
4. 如果需要使用华为云会议通讯录的情况下,在登录鉴权过程中还可以携带邮箱、姓名、电话号码等信息,这些信息将被写入华为云会议的通讯录中。
App ID的申请
- 使用华为云账号,登录华为云会议控制台。前提是该华为云账号已经购买了华为云会议服务或者已经绑定了华为云会议企业管理员账号,请参考“开发流程”章节中的“开发前准备”。
- 单击“应用管理”,再单击“创建应用”。在弹出的创建应用的输入框中输入第三方应用的名称及描述。
图3 创建应用
- 创建成功
单击“创建”后,会生成App ID和App Key。
图4 创建成功
- App Key是生成鉴权签名材料Signature的密钥,需要在第三方服务端妥善保存,不要泄露出去,否则会出现被盗用会议资源的风险。
- App Key只能保存在第三方服务端,不能保存在第三方终端中,否则很容易被反编译获取。
- App Key需要妥善保存,遗失后只能重置,不能找回。重新生成App Key原来的App Key将会在一个月后失效。
第三方服务集成Signature生成算法
App只在单个企业内使用的情况下,生成鉴权签名材料Signature的算法如下:
Signature = HexEncode(HMAC-SHA256((App ID + ":" + User ID + ":" + ExpireTime + ":" + Nonce), appKey))
App是SP开发的,需要在多个企业内使用的情况下,生成鉴权签名材料Signature的算法如下:
- 作为企业普通用户,对自己的会议管理:
Signature = HexEncode(HMAC-SHA256((App ID + ":" + CorpID + ":" + User ID + ":" + ExpireTime + ":" + Nonce), appKey))
- 作为企业管理员,对企业资源进行管理:
Signature = HexEncode(HMAC-SHA256((App ID + ":" + CorpID + "::" + ExpireTime + ":" + Nonce), appKey))
作为企业管理员时也可以带User ID,这个User ID必须具有管理员权限。
- 作为SP管理员,对SP资源进行管理(如创建企业或者分配企业资源等):
Signature = HexEncode(HMAC-SHA256((App ID + ":::" + ExpireTime + ":" + Nonce), appKey))
算法说明:
- HMAC-SHA256的输入数据是App ID、CorpID(可选)、User ID(可选)、ExpireTime、Nonce值,中间用“:”分隔。如d5e17******************489e:alice@ent01:1604020600:EycLQs************************nINuU1EBpQ
SP模式下即使User ID或者Corp ID和User ID不填的情况下,对应的“:”不能省略。
- HMAC-SHA256的密钥是App Key。如tZAe********q32T
- HMAC-SHA256生成的二进制数需要转换为十六进制字符串(HexEncode),上述的数据和密钥最终生成的Signature如下:2a8c780c***************************************************5a6c44f3c1b3c2455d
- ExpireTime:鉴权签名材料Signature的过期时间戳。单位是秒。比如当前的系统的时间戳是1604020000,Signature有效期设置成10分钟,则ExpireTime = 1604020000+10*60 = 1604020600
- Nonce:随机字符串,每次计算鉴权签名材料Signature时都必须不同。字符串长度为32~64字节
1. 由于鉴权签名材料Signature是有有效期的,所以第三方服务端的系统时间和华为云会议服务端的系统时间误差不能太大(小于ExpireTime中的有效期,如上例中的10分钟)。华为云会议服务端时间已经跟标准NTP时间同步。
2. ExpireTime为0,表示永远不过期。为了防止重放攻击,不建议将ExpireTime设置为0。
Java版本的Signature生成算法源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class HmacSHA256 { //十六进制字符集 private final static char[] DIGEST_ARRAYS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * 功能:Signature生成算法 * 输入参数: * 1.data: HMAC-SHA256的输入数据 * 2.key: App Key * 输出参数:十六进制字符串编码的HMAC-SHA256值 */ public static String encode(String data, String key) { byte[] hashByte; try { Mac sha256HMAC = Mac.getInstance("HmacSHA256"); SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"); sha256HMAC.init(secretKey); hashByte = sha256HMAC.doFinal(data.getBytes("UTF-8")); } catch (NoSuchAlgorithmException | UnsupportedEncodingException | InvalidKeyException e) { return null; } return bytesToHex(hashByte); } /** * 功能:byte类型数组转换成十六进制字符串 * 输入参数: * 1.bytes:被转换的字节数组 * 输出参数:十六进制字符串 */ private static String bytesToHex(byte[] bytes) { StringBuffer hexStr = new StringBuffer(); for (int i = 0; i < bytes.length; i++) { hexStr.append(DIGEST_ARRAYS[bytes[i] >>> 4 & 0X0F]); hexStr.append(DIGEST_ARRAYS[bytes[i] & 0X0F]); } return hexStr.toString(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import hmac from hashlib import sha256 class HmacSHA256: def __init__(self, sig_data, sig_key): self.data = sig_data self.key = sig_key def encode(self): try: sig_data = self.data.encode('utf-8') secret_key = self.key.encode('utf-8') signature = hmac.new(secret_key, sig_data, digestmod=sha256).hexdigest() except Exception as e: print (e) raise e return signature |
C++版本的Signature生成算法源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
#include <openssl/hmac.h> #include <string.h> #include <iostream> using namespace std; const int HMAC_ENCODE_SUCCESS = 0; const int HMAC_ENCODE_FAIL = -1; const int HMAC_SHA256_STR_LEN = 65; /** * 功能: 字节流转换成十六进制字符串 * 输入参数: * 1.input: 字节流指针 * 2.len: 字节流字节数 * 输出参数: * 3.output: 输出缓冲区 * 6.output_length: Signature字符串长度 * 返回值: * 0:失败 * 大于0的整数:转换后的字符串长度 */ int Byte2HexStr(char * output, unsigned char * input, unsigned int len) { if ((NULL == output) || (NULL == input)) { return 0; } unsigned int i = 0; for (i = 0; i < len; i++) { sprintf_s(output + 2*i, HMAC_SHA256_STR_LEN-2*i, "%x%x", (input[i] >> 4) & 0x0F, input[i]&0x0F); } *(output + 2*i) = '\0'; return 2*i; } /** * 功能: Signature生成算法 * 输入参数: * 1.key: App Key * 2.key_length: App Key的字符串长度 * 3.input: HMAC-SHA256的输入数据 * 4.input_length: HMAC-SHA256的输入数据的字符串长度 * 输出参数: * 5.output: Signature输出缓冲区 * 6.output_length: Signature字符串长度 * 返回值: * 0:成功 * -1:失败 */ int HmacEncode(const char * key, unsigned int key_length, const char * input, unsigned int input_length, char output[HMAC_SHA256_STR_LEN], unsigned int &output_length) { //计算HMAC_SHA256的字节流 const EVP_MD * engine = EVP_sha256(); unsigned char * byte_output = (unsigned char*)malloc(EVP_MAX_MD_SIZE); if (NULL == byte_output) { output_length = 0; return HMAC_ENCODE_FAIL; } unsigned int byte_output_length = 0; HMAC_CTX *ctx; ctx = HMAC_CTX_new(); HMAC_Init(ctx, key, strlen(key), engine); HMAC_Update(ctx, (unsigned char*)input, strlen(input)); HMAC_Final(ctx, byte_output, &byte_output_length); HMAC_CTX_free(ctx); //HMAC_SHA256的字节流转换成十六进制字符串 int ret = Byte2HexStr(output, byte_output, byte_output_length); free(byte_output); if (0 == ret) { output_length = 0; return HMAC_ENCODE_FAIL; } else { output_length = ret; return HMAC_ENCODE_SUCCESS; } } |
C++的Signature生成算法基于openssl 1.1.0以上版本实现。openssl库需要开发者自己编译和安装,请参考openssl官网。
JavaScript版本的Signature生成算法源代码
1 2 3 4 5 6 7 8 9 10 11 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"> </script> <script> /** * 1、重要提醒:生成Signature的算法不要放在前端代码中,否则会导致App Key泄露,从而导致企业资源被盗 * 2、本示例代码仅供调试时使用 */ function genSignature(data,appKey){ var sign = CryptoJS.HmacSHA256(data,appKey).toString(); return sign } </script> |
集成UI SDK的第三方客户端App ID鉴权
第三方客户端初始化SDK时,需要传入事先申请好的App ID。
- Android SDK的初始化接口,请参考《客户端SDK参考》中“Android SDK”的“初始化”章节。
- iOS SDK的初始化接口,请参考《客户端SDK参考》中“iOS SDK”的“初始化”章节。
- Windows SDK的初始化接口,请参考《客户端SDK参考》中“Windows SDK”的“初始化”章节。
- Mac SDK的初始化接口,请参考《客户端SDK参考》中“Mac SDK”的“初始化”章节。
第三方客户端登录时,从第三方服务端获取Signature、ExpireTime和Nonce后,调用终端SDK的Login接口完成鉴权。
第三方服务端App ID鉴权
第三方服务端通过App ID鉴权接口,完成App ID鉴权,并获取接入Token。请参考《服务端API参考》中的“执行App ID鉴权”章节。