Key防盗链
为保障直播资源不被非法盗用,您可以使用直播的Key防盗链功能,在原始推流或播放地址末尾加上鉴权信息。在主播请求直播推流或观众请求播放时,CDN会对其URL带的加密信息进行合法性判断,仅校验通过的请求会予以响应,其它非法的访问将予以拒绝。
工作原理

流程说明如下所示:
- 租户在直播控制台开启Key防盗链功能,并配置Key值和时长。
- 直播服务将租户配置Key值和时长下发到CDN节点中。
- 主播/观众通过租户提供的鉴权推流/播放URL向CDN请求推流或播放。
- CDN根据推流或播放URL中携带的鉴权信息校验请求的合法性,仅校验通过的请求会被允许。
注意事项
- 该功能为可选项,默认不启用。
- 启用该功能后,原始直播加速URL将无法使用,需要按规则生成合法的防盗链URL。
- 若防盗链URL过期,或者签名不能通过,直播流将播放失败,并返回“403 Forbidden”信息。
- 若需要关闭Key防盗链,请提交工单申请。
前提条件
- 已添加推流域名和播放域名,且已完成域名关联。
- 已在域名DNS服务商处完成CNAME解析配置。
开启Key防盗链
- 登录视频直播控制台。
- 在左侧导航树中选择 ,进入域名管理页面。
- 在需要配置鉴权信息的播放域名行单击“管理”。
- 在左侧导航树中选择 。
- 选择 ,弹出“Key防盗链”对话框。
- 单击“开关”,如图2所示,配置Key防盗链参数。
- Key:鉴权key值,支持自定义设置,由16位的字母和数字组成。
- 时长:URL鉴权信息的超时时长,指的是鉴权信息中携带的请求时间与直播服务收到请求时的时间的最大差值,用于检查直播推流URL或者直播播放URL是否已过期,单位:秒,范围限制:1分钟-7天。
- 配置完成后,单击“确定”。
- 提交工单申请Key防盗链配置审核。
若您修改了Key防盗链相关参数,也需要提交工单重新审核,提交工单时,需要提交的信息如表1所示。
表1 Key防盗链配置审核 信息
描述
推流域名
需要配置Key防盗链的直播推流域名。
示例:test-push.example.com
播放域名
需要配置Key防盗链的直播播放域名。
示例:test-play.example.com
Key值
请填写为4中设置的Key值。
示例:9wAynnEtvouJwJMs
时长
请填写为4中设置的时长。
示例:120
审核通过后,您才可以通过鉴权URL进行推流或播放。请参见生成鉴权URL获取携带鉴权信息的推流和播放地址。
生成鉴权URL
鉴权URL的组成如下所示:
原始地址?auth_info=加密串.EncodedIV
- LiveID=<AppName>+"/"+<StreamName>
- 加密串=UrlEncode(Base64(AES128(<Key>,"$"+<Timestamp>+"$"+<LiveID>+"$"+<CheckLevel>)))
- EncodedIV=Hex(加密使用的IV)
算法中各加密参数说明如表2所示。
参数名 |
描述 |
---|---|
AppName |
应用名称,与推流或播放地址中的AppName一致。 |
StreamName |
流名称,与推流或播放地址中的StreamName一致。 |
Key |
鉴权Key值,开启Key防盗链中配置的Key值。 |
LiveID |
直播流ID,用于标识唯一的直播流,由AppName和StreamName组成。 LiveID=<AppName>+"/"+<StreamName> |
Timestamp |
鉴权参数生成的UTC时间,格式为“yyyyMMddHHmmss”,用于检查鉴权参数是否已过期,即Timestamp和当前时间差值的绝对值是否大于配置的超时时长。 |
CheckLevel |
检查级别。取值为3或者5。
|
IV |
CBC对称加密算法依赖IV向量,随机生成的16位数字和字母组合,IV值长度为128位;CBC模式,PKCS7填充。 |
鉴权URL示例
rtmp://live.huaweitest1.com/live/8712345?auth_info=LpB4kdZfnOwfbpIgYVo4ABAU6CRUmV00OEARLlC7NLs%3D.79436d453636364e335941713330534e
其中,LpB4kdZfnOwfbpIgYVo4ABAU6CRUmV00OEARLlC7NLs%3D为加密串,79436d453636364e335941713330534e为EncodedIV。
代码示例
以下以Java demo为例,生成鉴权URL中的“加密串.EncodedIV”。
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 | import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class Main {
public static void main(String[] args) {
// data="$"+<Timestamp>+"$"+<LiveID>+"$"+<CheckLevel>,具体请参见“鉴权URL生成”
String data = "$20190428110000$live/stream01$3";
// 随机生成的16位数字和字母组合
byte[] ivBytes = "yCmE666N3YAq30SN".getBytes();
//在直播控制台配置的Key值
byte[] key = "MyLiveKeyValue01".getBytes();
String msg = aesCbcEncrypt(data, ivBytes, key);
try {
System.out.println(URLEncoder.encode(msg, "UTF-8") + "." + bytesToHexString(ivBytes));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
private static String aesCbcEncrypt(String data, byte[] ivBytes, byte[] key) {
try {
SecretKeySpec sk = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
if (ivBytes != null) {
cipher.init(Cipher.ENCRYPT_MODE, sk, new IvParameterSpec(ivBytes));
} else {
cipher.init(Cipher.ENCRYPT_MODE, sk);
}
return Base64.encode(cipher.doFinal(data.getBytes("UTF-8")));
} catch (Exception e) {
return null;
}
}
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if ((src == null) || (src.length <= 0)) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
}
|
以下是Base64类,用于将加密串进行编码。
public class Base64 { /** Base64编码表。*/ private static char base64Code[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',}; /** * 构造方法私有化,防止实例化。 */ private Base64() { super(); } /** * Base64编码。将字节数组中字节3个一组编码成4个可见字符。 * @param bytes 需要被编码的字节数据。 * @return 编码后的Base64字符串。 */ public static String encode(byte[] bytes) { int a = 0; // 按实际编码后长度开辟内存,加快速度 StringBuffer buffer = new StringBuffer(((bytes.length - 1) / 3) << 2 + 4); // 进行编码 for (int i = 0; i < bytes.length; i++) { a |= (bytes[i] << (16 - i % 3 * 8)) & (0xff << (16 - i % 3 * 8)); if (i % 3 == 2 || i == bytes.length - 1) { buffer.append(Base64.base64Code[(a & 0xfc0000) >>> 18]); buffer.append(Base64.base64Code[(a & 0x3f000) >>> 12]); buffer.append(Base64.base64Code[(a & 0xfc0) >>> 6]); buffer.append(Base64.base64Code[a & 0x3f]); a = 0; } } // 对于长度非3的整数倍的字节数组,编码前先补0,编码后结尾处编码用=代替, // =的个数和短缺的长度一致,以此来标识出数据实际长度 if (bytes.length % 3 > 0) { buffer.setCharAt(buffer.length() - 1, '='); } if (bytes.length % 3 == 1) { buffer.setCharAt(buffer.length() - 2, '='); } return buffer.toString(); } }
