# CDN 推流鉴权

- - -

## 推流鉴权简介

为防止攻击者盗取您的推流 URL 地址在别处推流使用、或伪造您的服务器生成推流 URL 地址，从而造成流量损失，您可以通过 [ZEGO 控制台](https://console.zego.im/) 自助配置 `推流鉴权`，当开启鉴权后，需要在推流 URL 地址中拼接相关的鉴权参数，否则无法推流。

<Warning title="注意">


- 您从 [ZEGO 控制台](https://console.zego.im/) 配置 `推流鉴权` 后，请妥善保管 **KEY**，勿轻易泄露，以防被攻击者获取，造成损失。
- 若您是 RTC 转推 CDN、或混流转推 CDN，ZEGO 服务器会自动帮您生成、并拼接 URL 地址的参数，您无需再自行生成；若您是通过第三方平台推流，则需要自行生成和拼接相关参数。
</Warning>


使用时，请您根据自己的 AppID 主营业务地区，参考如下文档进行配置。

- AppID 主营业务地区为 `大陆`，请参考 [腾讯云鉴权说明](#腾讯云鉴权说明) 或 [华为云鉴权说明](#华为云鉴权说明)。
- AppID 主营业务地区为 `港澳台或海外`，请参考 [网宿云鉴权说明](#网宿云鉴权说明)。



## 腾讯云鉴权说明

开启 `推流鉴权` 后，完整的推流地址如下：

<Frame width="512" height="auto" caption="">
  <img src="https://doc-media.zego.im/sdk-doc/Pics/Express/authentication_tencent.png" />
</Frame>

其中，“domain” 和“接入点”，请在 “控制台 > 项目详情 > 服务配置 > CDN 服务 > CDN 相关地址” 获取。

<Frame width="512" height="auto" caption="">
  <img src="https://doc-media.zego.im/sdk-doc/Pics/Express/authentication_console.png" />
</Frame>

### 1 生成鉴权密钥

请前往 [ZEGO 控制台](https://console.zego.im/)，在您的 “项目配置 > 服务配置” 中，找到 `鉴权配置`，开启，在弹出的对话框中配置或自动生成推流鉴权 KEY。

其中，主 KEY 为必填、备 KEY 为选填。**我们建议您同时配置主 KEY 和备 KEY，如果主 KEY 泄露，可平滑切换到备 KEY，不影响您的业务使用。**

<Frame width="512" height="auto" caption="">
  <img src="https://doc-media.zego.im/sdk-doc/Pics/Express/authentication_tencent_1.png" />
</Frame>

### 2 生成 txTime

txTime，指该推流 URL 地址的有效期。

例如，当前的时间是 2018-12-29 11:13:45，期望新生成的 URL 是在 3 小时后失效，那么：

1. txTime 就可以设置为 2018-12-29 14:13:45。
2. 再将该时间转换成 Unix 时间戳格式（即 1546064025）。
3. 然后再转换成十六进制，以进一步压缩字符长度，得到 txTime = 5c271099（十六进制）。

### 3 生成 txSecret

txSecret 的生成方法是 `txSecret = MD5(KEY + StreamName + txTime)`。

其中：

- KEY：指您在 [生成鉴权密钥](#1-生成鉴权密钥) 中配置的加密 KEY。
- StreamName：流名称，用户自定义，用于标识直播流。
- txTime：指您在 [生成 txTime](#2-生成-txtime) 中生成的 txTime。
- MD5：标准的 MD5 单向不可逆哈希算法。

### 4 得到 URL 地址

按照以上步骤完成后，最终地址如下（示例地址，该地址仅作为格式参考，请勿直接线上使用）：

```url
rtmp://push-tencent1.zego.im/live/123?txSecret=235cec79bf9483439762ddfd491387e2&txTime=5c271099
```


## 网宿云鉴权说明

开启 `推流鉴权` 后，完整的推流地址如下：

<Frame width="512" height="auto" caption="">
  <img src="https://doc-media.zego.im/sdk-doc/Pics/Express/authentication_ws.png" />
</Frame>

其中，“domain” 和“接入点”，请在 “控制台 > 项目详情 > 服务配置 > CDN 服务 > CDN 相关地址” 获取。

<Frame width="512" height="auto" caption="">
  <img src="https://doc-media.zego.im/sdk-doc/Pics/Express/authentication_console.png" />
</Frame>

### 1 生成鉴权密钥

请前往 [ZEGO 控制台](https://console.zego.im/)，在您的 “项目配置 > 服务配置” 中，找到 `鉴权配置`，开启，在弹出的对话框中配置或自动生成推流鉴权 KEY。

其中，主 KEY 为必填、备 KEY 为选填。**我们建议您同时配置主 KEY 和备 KEY，如果主 KEY 泄露，可平滑切换到备 KEY，不影响您的业务使用。**

<Frame width="512" height="auto" caption="">
  <img src="https://doc-media.zego.im/sdk-doc/Pics/Express/authentication_tencent_1.png" />
</Frame>

### 2 生成 wsABStime

wsABStime，指该推流 URL 地址的有效期。

例如，当前的时间是 2018-12-29 11:13:45，期望新生成的 URL 是在 3 小时后失效，那么：

1. wsABStime 就可以设置为 2018-12-29 14:13:45。
2. 再将该时间转换成 Unix 时间戳格式（即 1546064025）。
3. 然后再转换成十六进制，以进一步压缩字符长度，得到 wsABStime = 5C271099（十六进制）。

### 3 生成 wsSecret

wsSecret 的生成方法是 `wsSecret = MD5(wsABStime + StreamName + KEY)`。

其中：

- wsABStime：指您在 [生成 wsABStime](#2-生成-wsabstime) 中生成的 wsABStime，例如 5C271099。
- StreamName：路径，格式为 “/接入点/streamID”，例如 /live/streamid123。
- KEY：指您在 [生成鉴权密钥](#1-生成鉴权密钥) 中配置的加密 KEY，例如 KEY123。
- MD5：标准的 MD5 单向不可逆哈希算法。

即 wsSecret = MD5(5C271099/live/streamid123KEY123)

### 4 得到 URL 地址

按照以上步骤完成后，最终地址如下（示例地址，该地址仅作为格式参考，请勿直接线上使用）：

```url
rtmp://push-ws1.zego.im/live/123?wsSecret=235cec79bf9483439762ddfd491387e2&wsABStime=5C271099
```


## 华为云鉴权说明

开启 `推流鉴权` 后，完整的推流地址如下：

<Frame width="512" height="auto" caption="">
  <img src="https://doc-media.zego.im/sdk-doc/Pics/Express/authentication_huawei.png" />
</Frame>

其中，“domain” 和“接入点”，请在 “控制台 > 项目详情 > 服务配置 > CDN 服务 > CDN 相关地址” 获取。

### 1 获取鉴权密钥

请联系 ZEGO 技术支持，配置华为云 CDN 推流鉴权的相关权限，获取鉴权密钥。

### 2 生成 hwTime

hwTime，指该推流 URL 地址的有效时长。

例如，当前的时间是 2018-12-29 11:13:45，期望新生成的 URL 是在 3 小时后失效，那么：

1. hwTime 就可以设置为 2018-12-29 14:13:45。
2. 再将该时间转换成 Unix 时间戳格式（即 1546064025）。
3. 然后再转换成十六进制，以进一步压缩字符长度，得到 hwTime = 5c271099（十六进制）。

### 3 生成 hwSecret

hwSecret 的生成方法是 `hwSecret = hmac_sha256（KEY, StreamName + hwTime）`。

其中：

- KEY：指您在 [获取鉴权密钥](#1-获取鉴权密钥) 中获取的鉴权密钥。
- StreamName：流名称，用户自定义，用于标识直播流。
- hwTime：指您在 [生成 hwTime](#2-生成-hwtime) 中生成的 hwTime。
- hmac_sha256：HMAC-SHA256 加密算法。

### 4 得到 URL 地址

按照以上步骤完成后，最终地址如下（示例地址，该地址仅作为格式参考，请勿直接线上使用）：

```url
rtmp://push-huawei1.zego.im/live/123?hwSecret=ce201856a0957413319e883c8ccae13602f01d3d91e21daf5161964cf708a6a8&hwTime=5c271099
```

## 鉴权参数生成示例代码

以下是支持三家厂商（腾讯云、网宿云、华为云）的统一鉴权参数生成示例代码：

<CodeGroup>

```go Go
package main

import (
    "crypto/hmac"
    "crypto/md5"
    "crypto/sha256"
    "fmt"
    "time"
)

// AuthResult 鉴权结果
type AuthResult struct {
    Secret string // 鉴权密钥
    Time   string // 时间参数
    URL    string // 完整推流URL
}

// GenerateCDNAuth 生成CDN鉴权参数
func GenerateCDNAuth(provider, key, streamName string, expireHours int) (*AuthResult, error) {
    // 1. 生成过期时间戳（十六进制）
    expireTime := time.Now().Add(time.Duration(expireHours) * time.Hour).Unix()
    timeHex := fmt.Sprintf("%x", expireTime)

    var secret, timeParam, url string

    switch provider {
    case "tencent":
        // 腾讯云: txSecret = MD5(KEY + StreamName + txTime)
        data := key + streamName + timeHex
        hash := md5.Sum([]byte(data))
        secret = fmt.Sprintf("%x", hash)
        timeParam = timeHex
        url = fmt.Sprintf("rtmp://push-tencent1.zego.im/live/%s?txSecret=%s&txTime=%s",
            streamName, secret, timeParam)

    case "wangsu":
        // 网宿云流名称需要包含接入点/live/前缀
        // 网宿云: wsSecret = MD5(wsABStime + StreamName + KEY)
        timeParam = strings.ToUpper(timeHex)
        data := timeParam + "/live/" + streamName + key
        hash := md5.Sum([]byte(data))
        secret = fmt.Sprintf("%x", hash)
        url = fmt.Sprintf("rtmp://push-ws1.zego.im/live/%s?wsSecret=%s&wsABStime=%s",
            streamName, secret, timeParam)

    case "huawei":
        // 华为云: hwSecret = hmac_sha256(KEY, StreamName + hwTime)
        data := streamName + timeHex
        h := hmac.New(sha256.New, []byte(key))
        h.Write([]byte(data))
        secret = fmt.Sprintf("%x", h.Sum(nil))
        timeParam = timeHex
        url = fmt.Sprintf("rtmp://push-huawei1.zego.im/live/%s?hwSecret=%s&hwTime=%s",
            streamName, secret, timeParam)

    default:
        return nil, fmt.Errorf("不支持的厂商: %s", provider)
    }

    return &AuthResult{
        Secret: secret,
        Time:   timeParam,
        URL:    url,
    }, nil
}

func main() {
    // 示例参数
    key := "your_auth_key"           // 鉴权密钥
    streamName := "123"              // 流名称
    expireHours := 3                 // 3小时后过期

    // 生成腾讯云鉴权
    if result, err := GenerateCDNAuth("tencent", key, streamName, expireHours); err == nil {
        fmt.Printf("腾讯云 - Secret: %s, Time: %s\n", result.Secret, result.Time)
        fmt.Printf("腾讯云 - URL: %s\n\n", result.URL)
    }

    // 生成网宿云鉴权
    if result, err := GenerateCDNAuth("wangsu", key, streamName, expireHours); err == nil {
        fmt.Printf("网宿云 - Secret: %s, Time: %s\n", result.Secret, result.Time)
        fmt.Printf("网宿云 - URL: %s\n\n", result.URL)
    }

    // 生成华为云鉴权
    if result, err := GenerateCDNAuth("huawei", key, streamName, expireHours); err == nil {
        fmt.Printf("华为云 - Secret: %s, Time: %s\n", result.Secret, result.Time)
        fmt.Printf("华为云 - URL: %s\n", result.URL)
    }
}
```

```java Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;

public class CDNAuth {

    // 鉴权结果类
    public static class AuthResult {
        private String secret;  // 鉴权密钥
        private String time;    // 时间参数
        private String url;     // 完整推流URL

        public AuthResult(String secret, String time, String url) {
            this.secret = secret;
            this.time = time;
            this.url = url;
        }

        // Getters
        public String getSecret() { return secret; }
        public String getTime() { return time; }
        public String getUrl() { return url; }
    }

    /**
     * 生成CDN鉴权参数
     * @param provider 厂商类型 ("tencent", "wangsu", "huawei")
     * @param key 鉴权密钥
     * @param streamName 流名称
     * @param expireHours 过期小时数
     * @return 鉴权结果
     */
    public static AuthResult generateCDNAuth(String provider, String key, String streamName,
                                           int expireHours) throws Exception {
        // 1. 生成过期时间戳（十六进制）
        long expireTime = System.currentTimeMillis() / 1000 + expireHours * 3600;
        String timeHex = Long.toHexString(expireTime);

        String secret, timeParam, url;

        switch (provider) {
            case "tencent":
                // 腾讯云: txSecret = MD5(KEY + StreamName + txTime)
                String data1 = key + streamName + timeHex;
                secret = md5Hash(data1);
                timeParam = timeHex;
                url = String.format("rtmp://push-tencent1.zego.im/live/%s?txSecret=%s&txTime=%s",
                    streamName, secret, timeParam);
                break;

            case "wangsu":
                // 网宿云流名称需要包含接入点/live/前缀
                // 网宿云: wsSecret = MD5(wsABStime + StreamName + KEY)
                timeParam = timeHex.toUpperCase();
                String data2 = timeParam + "/live/" + streamName + key;
                secret = md5Hash(data2);
                url = String.format("rtmp://push-ws1.zego.im/live/%s?wsSecret=%s&wsABStime=%s",
                    streamName, secret, timeParam);
                break;

            case "huawei":
                // 华为云: hwSecret = hmac_sha256(KEY, StreamName + hwTime)
                String data3 = streamName + timeHex;
                secret = hmacSha256(key, data3);
                timeParam = timeHex;
                url = String.format("rtmp://push-huawei1.zego.im/live/%s?hwSecret=%s&hwTime=%s",
                    streamName, secret, timeParam);
                break;

            default:
                throw new IllegalArgumentException("不支持的厂商: " + provider);
        }

        return new AuthResult(secret, timeParam, url);
    }

    // MD5哈希
    private static String md5Hash(String input) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] hash = md.digest(input.getBytes());
        StringBuilder result = new StringBuilder();
        for (byte b : hash) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }

    // HMAC-SHA256
    private static String hmacSha256(String key, String data) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256");
        mac.init(secretKeySpec);
        byte[] hash = mac.doFinal(data.getBytes());
        StringBuilder result = new StringBuilder();
        for (byte b : hash) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }

    public static void main(String[] args) {
        try {
            // 示例参数
            String key = "your_auth_key";        // 鉴权密钥
            String streamName = "123";           // 流名称
            int expireHours = 3;                 // 3小时后过期

            // 生成腾讯云鉴权
            AuthResult tencentResult = generateCDNAuth("tencent", key, streamName, expireHours);
            System.out.println("腾讯云 - Secret: " + tencentResult.getSecret() + ", Time: " + tencentResult.getTime());
            System.out.println("腾讯云 - URL: " + tencentResult.getUrl() + "\n");

            // 生成网宿云鉴权
            AuthResult wangsuResult = generateCDNAuth("wangsu", key, streamName, expireHours);
            System.out.println("网宿云 - Secret: " + wangsuResult.getSecret() + ", Time: " + wangsuResult.getTime());
            System.out.println("网宿云 - URL: " + wangsuResult.getUrl() + "\n");

            // 生成华为云鉴权
            AuthResult huaweiResult = generateCDNAuth("huawei", key, streamName, expireHours);
            System.out.println("华为云 - Secret: " + huaweiResult.getSecret() + ", Time: " + huaweiResult.getTime());
            System.out.println("华为云 - URL: " + huaweiResult.getUrl());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

```javascript NodeJS
const crypto = require('crypto');

/**
 * 生成CDN鉴权参数
 * @param {string} provider 厂商类型 ("tencent", "wangsu", "huawei")
 * @param {string} key 鉴权密钥
 * @param {string} streamName 流名称
 * @param {number} expireHours 过期小时数
 * @returns {Object} {secret, time, url}
 */
function generateCDNAuth(provider, key, streamName, expireHours) {
    // 1. 生成过期时间戳（十六进制）
    const expireTime = Math.floor(Date.now() / 1000) + expireHours * 3600;
    const timeHex = expireTime.toString(16);

    let secret, timeParam, url;

    switch (provider) {
        case 'tencent':
            // 腾讯云: txSecret = MD5(KEY + StreamName + txTime)
            const data1 = key + streamName + timeHex;
            secret = crypto.createHash('md5').update(data1).digest('hex');
            timeParam = timeHex;
            url = `rtmp://push-tencent1.zego.im/live/${streamName}?txSecret=${secret}&txTime=${timeParam}`;
            break;

        case 'wangsu':
            // 网宿云流名称需要包含/live/前缀
            // 网宿云: wsSecret = MD5(wsABStime + StreamName + KEY)
            timeParam = timeHex.toUpperCase();
            const data2 = timeParam + "/live/" + streamName + key;
            secret = crypto.createHash('md5').update(data2).digest('hex');
            url = `rtmp://push-ws1.zego.im/live/${streamName}?wsSecret=${secret}&wsABStime=${timeParam}`;
            break;

        case 'huawei':
            // 华为云: hwSecret = hmac_sha256(KEY, StreamName + hwTime)
            const data3 = streamName + timeHex;
            secret = crypto.createHmac('sha256', key).update(data3).digest('hex');
            timeParam = timeHex;
            url = `rtmp://push-huawei1.zego.im/live/${streamName}?hwSecret=${secret}&hwTime=${timeParam}`;
            break;

        default:
            throw new Error(`不支持的厂商: ${provider}`);
    }

    return {
        secret: secret,
        time: timeParam,
        url: url
    };
}

// 示例使用
const key = 'your_auth_key';        // 鉴权密钥
const streamName = '123';           // 流名称
const expireHours = 3;              // 3小时后过期

try {
    // 生成腾讯云鉴权
    const tencentResult = generateCDNAuth('tencent', key, streamName, expireHours);
    console.log(`腾讯云 - Secret: ${tencentResult.secret}, Time: ${tencentResult.time}`);
    console.log(`腾讯云 - URL: ${tencentResult.url}\n`);

    // 生成网宿云鉴权
    const wangsuResult = generateCDNAuth('wangsu', key, streamName, expireHours);
    console.log(`网宿云 - Secret: ${wangsuResult.secret}, Time: ${wangsuResult.time}`);
    console.log(`网宿云 - URL: ${wangsuResult.url}\n`);

    // 生成华为云鉴权
    const huaweiResult = generateCDNAuth('huawei', key, streamName, expireHours);
    console.log(`华为云 - Secret: ${huaweiResult.secret}, Time: ${huaweiResult.time}`);
    console.log(`华为云 - URL: ${huaweiResult.url}`);

} catch (error) {
    console.error('生成鉴权参数失败:', error.message);
}
```

```python Python
import hashlib
import hmac
import time
from typing import Dict, Any

def generate_cdn_auth(provider: str, key: str, stream_name: str,
                     expire_hours: int = 3) -> Dict[str, Any]:
    """
    生成CDN鉴权参数

    Args:
        provider: 厂商类型 ("tencent", "wangsu", "huawei")
        key: 鉴权密钥
        stream_name: 流名称
        expire_hours: 过期小时数

    Returns:
        dict: {secret, time, url}
    """
    # 1. 生成过期时间戳（十六进制）
    expire_time = int(time.time()) + expire_hours * 3600
    time_hex = hex(expire_time)[2:]  # 去掉 '0x' 前缀

    if provider == "tencent":
        # 腾讯云: txSecret = MD5(KEY + StreamName + txTime)
        data = key + stream_name + time_hex
        secret = hashlib.md5(data.encode('utf-8')).hexdigest()
        time_param = time_hex
        url = f"rtmp://push-tencent1.zego.im/live/{stream_name}?txSecret={secret}&txTime={time_param}"

    elif provider == "wangsu":
        # 网宿云流名称需要包含/live/前缀
        # 网宿云: wsSecret = MD5(wsABStime + StreamName + KEY)
        time_param = time_hex.upper()
        data = time_param + "/live/" + stream_name + key
        secret = hashlib.md5(data.encode('utf-8')).hexdigest()
        url = f"rtmp://push-ws1.zego.im/live/{stream_name}?wsSecret={secret}&wsABStime={time_param}"

    elif provider == "huawei":
        # 华为云: hwSecret = hmac_sha256(KEY, StreamName + hwTime)
        data = stream_name + time_hex
        secret = hmac.new(key.encode('utf-8'), data.encode('utf-8'), hashlib.sha256).hexdigest()
        time_param = time_hex
        url = f"rtmp://push-huawei1.zego.im/live/{stream_name}?hwSecret={secret}&hwTime={time_param}"

    else:
        raise ValueError(f"不支持的厂商: {provider}")

    return {
        "secret": secret,
        "time": time_param,
        "url": url
    }

# 示例使用
if __name__ == "__main__":
    # 示例参数
    key = "your_auth_key"           # 鉴权密钥
    stream_name = "123"             # 流名称
    expire_hours = 3                # 3小时后过期

    try:
        # 生成腾讯云鉴权
        tencent_result = generate_cdn_auth("tencent", key, stream_name, expire_hours)
        print(f"腾讯云 - Secret: {tencent_result['secret']}, Time: {tencent_result['time']}")
        print(f"腾讯云 - URL: {tencent_result['url']}\n")

        # 生成网宿云鉴权
        wangsu_result = generate_cdn_auth("wangsu", key, stream_name, expire_hours)
        print(f"网宿云 - Secret: {wangsu_result['secret']}, Time: {wangsu_result['time']}")
        print(f"网宿云 - URL: {wangsu_result['url']}\n")

        # 生成华为云鉴权
        huawei_result = generate_cdn_auth("huawei", key, stream_name, expire_hours)
        print(f"华为云 - Secret: {huawei_result['secret']}, Time: {huawei_result['time']}")
        print(f"华为云 - URL: {huawei_result['url']}")

    except Exception as e:
        print(f"生成鉴权参数失败: {e}")
```

```php PHP
<?php

/**
 * 生成CDN鉴权参数
 *
 * @param string $provider 厂商类型 ("tencent", "wangsu", "huawei")
 * @param string $key 鉴权密钥
 * @param string $streamName 流名称
 * @param int $expireHours 过期小时数
 * @return array ['secret' => string, 'time' => string, 'url' => string]
 */
function generateCDNAuth($provider, $key, $streamName, $expireHours = 3) {
    // 1. 生成过期时间戳（十六进制）
    $expireTime = time() + $expireHours * 3600;
    $timeHex = dechex($expireTime);

    switch ($provider) {
        case 'tencent':
            // 腾讯云: txSecret = MD5(KEY + StreamName + txTime)
            $data = $key . $streamName . $timeHex;
            $secret = md5($data);
            $timeParam = $timeHex;
            $url = "rtmp://push-tencent1.zego.im/live/{$streamName}?txSecret={$secret}&txTime={$timeParam}";
            break;

        case 'wangsu':
            // 网宿云流名称需要包含/live/前缀
            // 网宿云: wsSecret = MD5(wsABStime + StreamName + KEY)
            $timeParam = strtoupper($timeHex);
            $data = $timeParam . "/live/" . $streamName . $key;
            $secret = md5($data);
            $url = "rtmp://push-ws1.zego.im/live/{$streamName}?wsSecret={$secret}&wsABStime={$timeParam}";
            break;

        case 'huawei':
            // 华为云: hwSecret = hmac_sha256(KEY, StreamName + hwTime)
            $data = $streamName . $timeHex;
            $secret = hash_hmac('sha256', $data, $key);
            $timeParam = $timeHex;
            $url = "rtmp://push-huawei1.zego.im/live/{$streamName}?hwSecret={$secret}&hwTime={$timeParam}";
            break;

        default:
            throw new InvalidArgumentException("不支持的厂商: {$provider}");
    }

    return [
        'secret' => $secret,
        'time' => $timeParam,
        'url' => $url
    ];
}

// 示例使用
try {
    // 示例参数
    $key = 'your_auth_key';        // 鉴权密钥
    $streamName = '123';           // 流名称
    $expireHours = 3;              // 3小时后过期

    // 生成腾讯云鉴权
    $tencentResult = generateCDNAuth('tencent', $key, $streamName, $expireHours);
    echo "腾讯云 - Secret: {$tencentResult['secret']}, Time: {$tencentResult['time']}\n";
    echo "腾讯云 - URL: {$tencentResult['url']}\n\n";

    // 生成网宿云鉴权
    $wangsuResult = generateCDNAuth('wangsu', $key, $streamName, $expireHours);
    echo "网宿云 - Secret: {$wangsuResult['secret']}, Time: {$wangsuResult['time']}\n";
    echo "网宿云 - URL: {$wangsuResult['url']}\n\n";

    // 生成华为云鉴权
    $huaweiResult = generateCDNAuth('huawei', $key, $streamName, $expireHours);
    echo "华为云 - Secret: {$huaweiResult['secret']}, Time: {$huaweiResult['time']}\n";
    echo "华为云 - URL: {$huaweiResult['url']}\n";

} catch (Exception $e) {
    echo "生成鉴权参数失败: " . $e->getMessage() . "\n";
}

?>
```

```cpp C++
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <ctime>
#include <stdexcept>
#include <openssl/md5.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>

// 鉴权结果结构体
struct AuthResult {
    std::string secret;  // 鉴权密钥
    std::string time;    // 时间参数
    std::string url;     // 完整推流URL

    AuthResult(const std::string& s, const std::string& t, const std::string& u)
        : secret(s), time(t), url(u) {}
};

class CDNAuth {
public:
    /**
     * 生成CDN鉴权参数
     * @param provider 厂商类型 ("tencent", "wangsu", "huawei")
     * @param key 鉴权密钥
     * @param streamName 流名称
     * @param expireHours 过期小时数
     * @return 鉴权结果
     */
    static AuthResult generateCDNAuth(const std::string& provider, const std::string& key,
                                    const std::string& streamName, int expireHours = 3) {
        // 1. 生成过期时间戳（十六进制）
        time_t expireTime = time(nullptr) + expireHours * 3600;
        std::stringstream ss;
        ss << std::hex << expireTime;
        std::string timeHex = ss.str();

        std::string secret, timeParam, url;

        if (provider == "tencent") {
            // 腾讯云: txSecret = MD5(KEY + StreamName + txTime)
            std::string data = key + streamName + timeHex;
            secret = md5Hash(data);
            timeParam = timeHex;
            url = "rtmp://push-tencent1.zego.im/live/" + streamName +
                  "?txSecret=" + secret + "&txTime=" + timeParam;

        } else if (provider == "wangsu") {
            // 网宿云流名称需要包含/live/前缀
            // 网宿云: wsSecret = MD5(wsABStime + StreamName + KEY)
            timeParam = toUpperCase(timeHex);
            std::string data = timeParam + "/live/" + streamName + key;
            secret = md5Hash(data);
            url = "rtmp://push-ws1.zego.im/live/" + streamName +
                  "?wsSecret=" + secret + "&wsABStime=" + timeParam;

        } else if (provider == "huawei") {
            // 华为云: hwSecret = hmac_sha256(KEY, StreamName + hwTime)
            std::string data = streamName + timeHex;
            secret = hmacSha256(key, data);
            timeParam = timeHex;
            url = "rtmp://push-huawei1.zego.im/live/" + streamName +
                  "?hwSecret=" + secret + "&hwTime=" + timeParam;

        } else {
            throw std::invalid_argument("不支持的厂商: " + provider);
        }

        return AuthResult(secret, timeParam, url);
    }

private:
    // MD5哈希
    static std::string md5Hash(const std::string& input) {
        unsigned char digest[MD5_DIGEST_LENGTH];
        MD5((unsigned char*)input.c_str(), input.length(), digest);

        std::stringstream ss;
        for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
            ss << std::hex << std::setw(2) << std::setfill('0') << (int)digest[i];
        }
        return ss.str();
    }

    // HMAC-SHA256
    static std::string hmacSha256(const std::string& key, const std::string& data) {
        unsigned char hash[SHA256_DIGEST_LENGTH];
        unsigned int hashLen;

        HMAC(EVP_sha256(), key.c_str(), key.length(),
             (unsigned char*)data.c_str(), data.length(), hash, &hashLen);

        std::stringstream ss;
        for (unsigned int i = 0; i < hashLen; i++) {
            ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
        }
        return ss.str();
    }
};

int main() {
    try {
        // 示例参数
        std::string key = "your_auth_key";        // 鉴权密钥
        std::string streamName = "123";           // 流名称
        int expireHours = 3;                      // 3小时后过期

        // 生成腾讯云鉴权
        auto tencentResult = CDNAuth::generateCDNAuth("tencent", key, streamName, expireHours);
        std::cout << "腾讯云 - Secret: " << tencentResult.secret << ", Time: " << tencentResult.time << std::endl;
        std::cout << "腾讯云 - URL: " << tencentResult.url << std::endl << std::endl;

        // 生成网宿云鉴权
        auto wangsuResult = CDNAuth::generateCDNAuth("wangsu", key, streamName, expireHours);
        std::cout << "网宿云 - Secret: " << wangsuResult.secret << ", Time: " << wangsuResult.time << std::endl;
        std::cout << "网宿云 - URL: " << wangsuResult.url << std::endl << std::endl;

        // 生成华为云鉴权
        auto huaweiResult = CDNAuth::generateCDNAuth("huawei", key, streamName, expireHours);
        std::cout << "华为云 - Secret: " << huaweiResult.secret << ", Time: " << huaweiResult.time << std::endl;
        std::cout << "华为云 - URL: " << huaweiResult.url << std::endl;

    } catch (const std::exception& e) {
        std::cerr << "生成鉴权参数失败: " << e.what() << std::endl;
    }

    return 0;
}
```

```csharp .NET
using System;
using System.Security.Cryptography;
using System.Text;

public class AuthResult
{
    public string Secret { get; set; }  // 鉴权密钥
    public string Time { get; set; }    // 时间参数
    public string Url { get; set; }     // 完整推流URL

    public AuthResult(string secret, string time, string url)
    {
        Secret = secret;
        Time = time;
        Url = url;
    }
}

public class CDNAuth
{
    /// <summary>
    /// 生成CDN鉴权参数
    /// </summary>
    /// <param name="provider">厂商类型 ("tencent", "wangsu", "huawei")</param>
    /// <param name="key">鉴权密钥</param>
    /// <param name="streamName">流名称</param>
    /// <param name="expireHours">过期小时数</param>
    /// <returns>鉴权结果</returns>
    public static AuthResult GenerateCDNAuth(string provider, string key, string streamName,
                                           int expireHours = 3)
    {
        // 1. 生成过期时间戳（十六进制）
        var expireTime = DateTimeOffset.UtcNow.AddHours(expireHours).ToUnixTimeSeconds();
        var timeHex = expireTime.ToString("x");

        string secret, timeParam, url;

        switch (provider)
        {
            case "tencent":
                // 腾讯云: txSecret = MD5(KEY + StreamName + txTime)
                var data1 = key + streamName + timeHex;
                secret = ComputeMD5Hash(data1);
                timeParam = timeHex;
                url = $"rtmp://push-tencent1.zego.im/live/{streamName}?txSecret={secret}&txTime={timeParam}";
                break;

            case "wangsu":
                // 网宿云流名称需要包含/live/前缀
                // 网宿云: wsSecret = MD5(wsABStime + StreamName + KEY)
                timeParam = timeHex.ToUpper();
                var data2 = timeParam + "/live/" + streamName + key;
                secret = ComputeMD5Hash(data2);
                url = $"rtmp://push-ws1.zego.im/live/{streamName}?wsSecret={secret}&wsABStime={timeParam}";
                break;

            case "huawei":
                // 华为云: hwSecret = hmac_sha256(KEY, StreamName + hwTime)
                var data3 = streamName + timeHex;
                secret = ComputeHmacSha256(key, data3);
                timeParam = timeHex;
                url = $"rtmp://push-huawei1.zego.im/live/{streamName}?hwSecret={secret}&hwTime={timeParam}";
                break;

            default:
                throw new ArgumentException($"不支持的厂商: {provider}");
        }

        return new AuthResult(secret, timeParam, url);
    }

    // MD5哈希
    private static string ComputeMD5Hash(string input)
    {
        using var md5 = MD5.Create();
        var inputBytes = Encoding.UTF8.GetBytes(input);
        var hashBytes = md5.ComputeHash(inputBytes);

        var sb = new StringBuilder();
        foreach (var b in hashBytes)
        {
            sb.Append(b.ToString("x2"));
        }
        return sb.ToString();
    }

    // HMAC-SHA256
    private static string ComputeHmacSha256(string key, string data)
    {
        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
        var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));

        var sb = new StringBuilder();
        foreach (var b in hashBytes)
        {
            sb.Append(b.ToString("x2"));
        }
        return sb.ToString();
    }

    public static void Main()
    {
        try
        {
            // 示例参数
            var key = "your_auth_key";        // 鉴权密钥
            var streamName = "123";           // 流名称
            var expireHours = 3;              // 3小时后过期

            // 生成腾讯云鉴权
            var tencentResult = GenerateCDNAuth("tencent", key, streamName, expireHours);
            Console.WriteLine($"腾讯云 - Secret: {tencentResult.Secret}, Time: {tencentResult.Time}");
            Console.WriteLine($"腾讯云 - URL: {tencentResult.Url}\n");

            // 生成网宿云鉴权
            var wangsuResult = GenerateCDNAuth("wangsu", key, streamName, expireHours);
            Console.WriteLine($"网宿云 - Secret: {wangsuResult.Secret}, Time: {wangsuResult.Time}");
            Console.WriteLine($"网宿云 - URL: {wangsuResult.Url}\n");

            // 生成华为云鉴权
            var huaweiResult = GenerateCDNAuth("huawei", key, streamName, expireHours);
            Console.WriteLine($"华为云 - Secret: {huaweiResult.Secret}, Time: {huaweiResult.Time}");
            Console.WriteLine($"华为云 - URL: {huaweiResult.Url}");

        }
        catch (Exception e)
        {
            Console.WriteLine($"生成鉴权参数失败: {e.Message}");
        }
    }
}
```

</CodeGroup>

<Content />