# Key防盗链
为保障直播资源不被非法盗用，您可以使用直播的Key防盗链功能，在原始推流或播放地址末尾加上鉴权信息。在主播请求直播推流或观众请求播放时，CDN会对其URL带的加密信息进行合法性判断，仅校验通过的请求会予以响应，其它非法的访问将予以拒绝。
若您有其它自定义防盗链规则的需求，请您[提交工单](https://console.huaweicloud.com/ticket/#/ticketindex/createIndex)与华为云技术客服联系。
#### 工作原理
图1Key防盗链工作原理   
![](https://support.huaweicloud.com/medialive-live/zh-cn_image_0000001704063864.png "点击放大")
流程说明如下所示：
1. 租户在直播控制台开启Key防盗链功能，并配置鉴权方式、Key值和时长。
2. 直播服务将租户配置的鉴权方式、Key值和时长下发到CDN节点中。
3. 主播/观众通过租户提供的鉴权推流/播放URL向CDN请求推流或播放。
4. CDN根据推流或播放URL中携带的鉴权信息校验请求的合法性，仅校验通过的请求会被允许。
 
#### 注意事项
- Key防盗链默认不展示，如需使用，需[提交工单](https://console.huaweicloud.com/ticket/#/ticketindex/createIndex)申请使用权限，界面才会展示Key防盗链功能。
- 该功能为可选项，默认不启用。启用该功能后，原始直播加速URL将无法使用，需要按规则生成合法的防盗链URL。
- 播放域名Key防盗链仅支持D类鉴权，不支持ABC类鉴权，且针对D类鉴权仅支持HLS，暂不支持DASH和MSS协议输出内容。
- 如果推流域名的推流协议为SRT，暂不支持配置Key防盗链和IP黑白名单，配置入口会自动屏蔽。SRT协议的推流域名如需实现防盗链，可通过创建频道时配置"CIDR IP 白名单"和"主输入解密参数"（开启"是否解密"）参数来实现，详见[创建频道](https://support.huaweicloud.com/medialive-live/live_25_0031.html)。
- 建议推流与播放鉴权使用不同的Key值，以增强安全性。若防盗链URL过期，或者签名不能通过，直播流将播放失败，并返回"403 Forbidden"信息。
- 针对RTMP、FLV这类长连接业务，只有服务端收到用户请求时，才进行防盗链参数校验，校验通过后可以持续播放。
- 针对HLS这类业务，用户播放后会携带相同的防盗链参数，持续发起请求。一旦防盗链参数过期，服务端便会因校验不通过，而拒绝访问，导致播放中断。 所以建议这类业务，适当调整鉴权过期时间，避免因时间过短，而中途就播放失败。示例：如果预估HLS播放时长每次都在1小时以内，可设置过期时间为3600秒。
  
 
#### 前提条件
- 已[添加推流域名和播放域名](https://support.huaweicloud.com/medialive-live/live_25_0014.html)。
- 已在域名DNS服务商处完成[CNAME解析配置](https://support.huaweicloud.com/medialive-live/live_25_0017.html)。
 
 #### 开启Key防盗链
1. 登录[视频直播控制台](https://console.huaweicloud.com/live2/#/live/home)。
2. 在左侧导航栏中，选择"域名管理"，进入域名管理页面。
3. 在需要配置鉴权信息的域名行单击"管理"。
4. 选择"鉴权配置"页签，单击"Key防盗链"右侧的编辑图标，界面右侧弹出"Key防盗链"对话框。
5. 单击"开关"，配置Key防盗链参数，如[图2]所示。
   
   推流域名支持ABCD类型4种鉴权方式，播放域名仅支持D类型的鉴权方式，下图以推流域名的截图为例。
   图2配置Key防盗链   
   ![](https://support.huaweicloud.com/medialive-live/zh-cn_image_0000001752023069.png "点击放大")
   表1Key防盗链参数说明 
   | 参数名 | 描述                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
   |:---|:---|
   | 类型   | 计算鉴权串的方式，可选为：方式A、方式B、方式C或方式D。 AB鉴权方式：采用MD5信息摘要算法，具体实现方法请参见[鉴权方式A]和[鉴权方式B]。 C鉴权方式：采用对称加密算法，具体实现方法请参见[鉴权方式C]。 D鉴权方式：采用HMAC-SHA256算法，具体实现方法请参见[鉴权方式D]。 说明： - 鉴权方式ABC存在安全风险，鉴权方式D拥有更高的安全性，建议您优先使用鉴权方式D。  - 播放域名Key防盗链仅支持D类鉴权，不支持ABC类鉴权，且针对D类鉴权仅支持HLS，暂不支持DASH和MSS协议输出内容。   |
   | Key | 鉴权key值。 - 支持自定义设置，由32位的字母和数字组成。  - 支持自动生成。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
   | 时长 | URL鉴权信息的超时时长，指的是鉴权信息中携带的请求时间与直播服务收到请求时的时间的最大差值，用于检查直播推流URL或者直播播放URL是否已过期，单位：秒，范围限制：1分钟-30天。 说明： - 针对RTMP、FLV这类长连接业务，只有服务端收到用户请求时，才进行防盗链参数校验，校验通过后可以持续播放。  - 针对HLS这类业务，用户播放后会携带相同的防盗链参数，持续发起请求。一旦防盗链参数过期，服务端便会因校验不通过，而拒绝访问，导致播放中断。 所以建议这类业务，适当调整鉴权过期时间，避免因时间过短，而中途就播放失败。示例：如果预估HLS播放时长每次都在1小时以内，可设置过期时间为3600秒。                                                                                                                                   |
      
   
   
6. 配置完成后，单击"确定"。
7. 通过以下方式获取鉴权地址。 
   - 手动拼接：根据配置的鉴权**类型** 拼接对应的鉴权地址，各鉴权类型对应的鉴权地址拼接方法请分别参见[鉴权方式A]、[鉴权方式B]、[鉴权方式C]和[鉴权方式D]。
   
   - 自动拼接：RTMP协议的推流域名，还支持使用防盗链地址生成工具快速生成鉴权地址，推荐优先使用此方式生成，操作详见[防盗地址生成](https://support.huaweicloud.com/medialive-live/live_25_0051.html)。
   
   
   
   
8. 验证防盗链功能。 
   使用第三方直播推拉流工具，通过鉴权推流地址和播放地址进行验证，若原始推流地址和播放地址无法成功推流和播放，使用鉴权推流地址和播放地址能成功推流和播放，则表示Key防盗链生效。
   
   
 
 #### 鉴权方式A
![](https://support.huaweicloud.com/medialive-live/public_sys-resources/caution_3.0-zh-cn.png)
- 推流域名仅RTMP协议支持鉴权方式A，播放域名均不支持鉴权方式A。
- 自动拼接：RTMP协议的推流域名，还支持使用防盗链地址生成工具快速生成鉴权地址，推荐优先使用此方式生成，操作详见[防盗地址生成](https://support.huaweicloud.com/medialive-live/live_25_0051.html)。
 
鉴权方式A主要通过Key、timestamp、rand（随机数）、uid（设置为0）和URL计算鉴权串。
**鉴权URL格式**
```
原始URL?auth_key={timestamp}-{rand}-{uid}-{md5hash}
```
**md5hash** 的计算公式：
```
sstring = "{URI}-{Timestamp}-{rand}-{uid}-{Key}"
HashValue = md5sum(sstring)
```
表2鉴权字段描述 
| 字段      | 描述                                                                                                                                                                                                                                             |
|:---|:---|
| timestamp | 用户定义的有效访问时间起始点，值为1970年1月1日以来的当前时间秒数 。十进制或者十六进制整数。 示例：1592639100（即2020-06-20 15:45）                                                                                                        |
| 时长      | 鉴权URL有效的时间长度。 若设置的有效时间为1800s，则用户可在从timestamp开始的1800s内允许访问直播地址。超出该区间，鉴权失败。 示例：若设置的访问时间为2020-6-30 00：00：00，则链接真正失效时间为2020-6-30 00：30：00。 |
| rand    | 随机数，建议使用UUID，不能包含中划线"-"。 示例：477b3bbc253f467b8def6711128c7bec                                                                                                                                |
| uid      | userID。暂未使用，直接设置成0即可。                                                                                                                                                                                                                            |
| md5hash | 通过md5算法计算出来的验证串，数字0-9和小写英文字母a-z混合组成，固定长度32。 ``` sstring = "{URI}-{Timestamp}-{rand}-{uid}-{Key}" HashValue = md5sum(sstring) ```                                                                                                                |
| URI    | 指原始URL中从域名后开始到问号前结束。示例：如果原始URL地址为"rtmp://live-push.example.com/live/huaweitest?request_source=ott\&channel_id=huaweitest"，则URI为"/live/huaweitest"。                                                                                                                     |
| Key     | 在控制台设置的防盗链Key值，具体请参见[开启Key防盗链]。                                                                                                                                                      |
   
**鉴权URL示例**
推流鉴权地址的生成方法如下所示：
```
原始URL：rtmp://live-push.example.com/live/huaweitest?request_source=ott&channel_id=huaweitest
timestamp：1592639100
时长：1800s
Key：GCTbw44s6MPLh4GqgDpnfuFHgy25Enly
rand：477b3bbc253f467b8def6711128c7bec
uid：0
URI：/live/huaweitest
```
根据计算公式，得到**md5hash** 。
```
HashValue = md5sum("/live/huaweitest-1592639100-477b3bbc253f467b8def6711128c7bec-0-GCTbw44s6MPLh4GqgDpnfuFHgy25Enly") = 1832e24276a08e180152c9c8a98ff322
```
则鉴权推流地址为：
```
rtmp://live-push.example.com/live/huaweitest?request_source=ott&channel_id=huaweitest&auth_key=1592639100-477b3bbc253f467b8def6711128c7bec-0-1832e24276a08e180152c9c8a98ff322
```
 #### 鉴权方式B
![](https://support.huaweicloud.com/medialive-live/public_sys-resources/caution_3.0-zh-cn.png)
- 推流域名仅RTMP协议支持鉴权方式B，播放域名均不支持鉴权方式B。
- 自动拼接：RTMP协议的推流域名，还支持使用防盗链地址生成工具快速生成鉴权地址，推荐优先使用此方式生成，操作详见[防盗地址生成](https://support.huaweicloud.com/medialive-live/live_25_0051.html)。
 
鉴权方式B主要通过Key、timestamp和StreamName计算鉴权串。
**鉴权URL格式**
```
原始URL?txSecret=md5(Key + StreamName + txTime)&txTime=hex(timestamp)
```
表3鉴权字段描述 
| 字段         | 描述                                                                                                                                                                                                                     |
|:---|:---|
| txTime   | 播放URL的有效时间，为Unix时间戳的十六进制结果。 如果当前txTime的值大于当前请求的时间则可以正常播放，否则播放会被后台拒绝。 示例：5eed5888（即2020.06.20 08:30:00） |
| Key        | 在控制台设置的防盗链Key值，具体请参见[开启Key防盗链]。                                                                                                                            |
| txSecret | URL中的加密参数。 通过将key，StreamName，txTime依次拼接的字符串进行MD5加密算法得出。 ``` txSecret = md5（Key + StreamName + txTime） ```                                                         |
| 时长        | 鉴权URL的有效时间长度。 若**txTime**设置为当前时间，有效时间设置为1249s，则播放URL过期时间为当前时间+ 1249s。                                                                                          |
   
**鉴权URL示例**
推流鉴权地址的生成方法如下所示：
```
原始URL：rtmp://live-push.example.com/live/huaweitest?request_source=ott&channel_id=huaweitest
Key：GCTbw44s6MPLh4GqgDpnfuFHgy25Enly
StreamName：huaweitest
txTime：5eed5888
时长：1249s
```
根据计算公式，得到**txSecret** 。
```
txSecret = md5(GCTbw44s6MPLh4GqgDpnfuFHgy25Enlyhuaweitest5eed5888) = 1f5b30ca84581f14efd1f7aa39def2e3
```
则鉴权推流地址为：
```
rtmp://live-push.example.com/live/huaweitest?request_source=ott&channel_id=huaweitest&txSecret=1f5b30ca84581f14efd1f7aa39def2e3&txTime=5eed5888
```
 #### 鉴权方式C
![](https://support.huaweicloud.com/medialive-live/public_sys-resources/caution_3.0-zh-cn.png)
- 推流域名仅RTMP协议支持鉴权方式C，播放域名均不支持鉴权方式C。
- 自动拼接：RTMP协议的推流域名，还支持使用防盗链地址生成工具快速生成鉴权地址，推荐优先使用此方式生成，操作详见[防盗地址生成](https://support.huaweicloud.com/medialive-live/live_25_0051.html)。
 
鉴权方式C主要通过Key、Timestamp、AppName、StreamName和CheckLevel计算鉴权串。
**鉴权URL格式**
```
原始URL?auth_info=加密串.EncodedIV
```
鉴权字段的生成算法如下所示，具体代码示例请参考[代码示例]。
- **LiveID**= \<AppName\>+"/"+\<StreamName\>
- **加密串** = UrlEncode(Base64(AES128(\<Key\>,"$"+\<Timestamp\>+"$"+\<LiveID\>+"$"+\<CheckLevel\>)))
- **EncodedIV**= Hex(加密使用的IV)
 
算法中各加密参数说明如[表4]所示。
 表4加密参数说明 
| 字段           | 描述                                                                                                                                                                                                                                                                                                                                                                                         |
|:---|:---|
| AppName    | 应用名称，与推流或播放地址中的AppName一致。                                                                                                                                                                                                                                                                                                                                                                  |
| StreamName   | 流名称，与推流或播放地址中的StreamName一致。                                                                                                                                                                                                                                                                                                                                                                  |
| Key        | 在控制台设置的防盗链Key值，具体请参见[开启Key防盗链]。                                                                                                                                                                                                                                                                                                   |
| LiveID       | 直播流ID，用于标识唯一的直播流，由AppName和StreamName组成。 ``` LiveID = <AppName>+"/"+<StreamName> ```                                                                                                                                                                                                                                                                                                        |
| Timestamp  | 鉴权参数生成的UTC时间，格式为"yyyyMMddHHmmss"，用于检查鉴权参数是否已过期，即Timestamp和当前时间差值的绝对值是否大于配置的超时时长。                                                                                                                                                                                                                                                                                                          |
| CheckLevel | 检查级别。取值为3或者5。 - CheckLevel＝3，只检查LiveID是否匹配，不检查鉴权URL是否过期。  - CheckLevel＝5，检查LiveID是否匹配，Timestamp是否超时。   |
| IV           | CBC对称加密算法依赖IV向量，随机生成的16位数字和字母组合，IV值长度为128位；CBC模式，PKCS7填充。                                                                                                                                                                                                                                                                                                                                  |
   
**鉴权URL示例**
推流鉴权地址的生成方法如下所示：
```
原始URL：rtmp://live-push.example.com/live/huaweitest?request_source=ott&channel_id=huaweitest
AppName：live
StreamName：huaweitest
Key：GCTbw44s6MPLh4GqgDpnfuFHgy25Enly
LiveID：live/huaweitest
Timestamp：20190428110000
CheckLevel：3
IV：yCmE666N3YAq30SN
```
根据计算公式，得到"加密串"和"EncodedIV"
```
加密串 = I90KW7GhxOMwoy5yaeKMSk%2FsLt08T4Wlc6avfPBz9FQGlHRFOgkTOGHXWsXfL44x.
EncodIV = 79436d453636364e335941713330534e
```
则鉴权推流地址为：
```
rtmp://live-push.example.com/live/huaweitest?request_source=ott&channel_id=huaweitest&auth_info=I90KW7GhxOMwoy5yaeKMSk%2FsLt08T4Wlc6avfPBz9FQGlHRFOgkTOGHXWsXfL44x.79436d453636364e335941713330534e
```
 #### 鉴权方式D
![](https://support.huaweicloud.com/medialive-live/public_sys-resources/caution_3.0-zh-cn.png)
- 推流域名仅RTMP协议支持鉴权方式D，播放域名仅HLS协议支持鉴权方式D。
- 自动拼接：RTMP协议的推流域名，还支持使用防盗链地址生成工具快速生成鉴权地址，推荐优先使用此方式生成，操作详见[防盗地址生成](https://support.huaweicloud.com/medialive-live/live_25_0051.html)。
 
鉴权方式D主要通过Key、timestamp和StreamName计算鉴权串。
**鉴权URL格式**
```
原始URL?hwSecret=hmac_sha256(Key, StreamName + hwTime)&hwTime=hex(timestamp)
```
表5鉴权字段描述 
| 字段        | 描述                                                                                                                                                                                                                      |
|:---|:---|
| hwTime      | 播放URL的有效时间，为Unix时间戳的十六进制结果。 如果当前hwTime+时长的值大于当前请求的时间则可以正常播放，否则播放会被后台拒绝。 示例：5eed5888（即2020.06.20 08:30:00） |
| Key        | 在控制台设置的防盗链Key值，具体请参见[开启Key防盗链]。                                                                                                                             |
| hwSecret | URL中的加密参数。 以Key和StreamName+hwTime为参数进行HMAC-SHA256加密算法得出。 ``` hwSecret = hmac_sha256（Key, StreamName + hwTime） ```                                                 |
| 时长         | 鉴权URL的有效时间长度。 若**hwTime**设置为当前时间，有效时间设置为1249s，则播放URL过期时间为当前时间+ 1249s。                                                                                             |
   
**鉴权URL示例**
以生成播放鉴权地址为例，推流鉴权地址的生成与播放鉴权地址的生成同理。
```
原始URL：https://live-play.example.com/{channelld}/hls/{unique_string}/index.m3u8
Key：GCTbw44s6MPLh4GqgDpnfuFHgy25Enly
StreamName：index
hwTime：5eed5888
时长：1249s
```
根据计算公式，得到**hwSecret** 。
```
hwSecret = hmac_sha256(GCTbw44s6MPLh4GqgDpnfuFHgy25Enly, index5eed5888) = 63eb41e0c5c8d8f8058aa83488901ad279645217f7099a2bcdef4f0044aa5b4f
```
则鉴权播放地址为：
```
https://live-play.example.com/{channelld}/hls/{unique_string}/index.m3u8?hwSecret=63eb41e0c5c8d8f8058aa83488901ad279645217f7099a2bcdef4f0044aa5b4f&hwTime=5eed5888
```
 #### 代码示例
以下为鉴权方式C的鉴权串生成代码示例。
```
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>，具体请参见“鉴权方式C”
                String data = "$20190428110000$live/stream01$3";
                // 随机生成的16位数字和字母组合
byte[] ivBytes = "yCmE666N3YAq30SN".getBytes();
                //在直播控制台配置的Key值
byte[] key = "GCTbw44s6MPLh4GqgDpnfuFHgy25Enly".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();
    }
}
```
