全部產品
Search
文件中心

ApsaraVideo VOD:HLS標準加密

更新時間:May 07, 2025

HLS標準加密採用AES_128演算法,能夠適配所有HLS播放器,並需結合密鑰管理與令牌服務,廣泛應用於線上教育、獨播劇等高安全需求領域。本文將介紹視頻的HLS加密與解密播放方法。

工作原理

ApsaraVideo for VOD採用信封加密技術,業務方通過阿里雲KMS產生資料密鑰(DK)和信封密鑰(EDK);使用DK加密視頻後,同時儲存加密視頻和(EDK)。終端播放時通過解密服務擷取(DK)進行視頻解密播放。

如果您需要對解密地址進行安全驗證,可以通過開啟M3U8標準加密改寫(預設改寫參數名為MtsHlsUriToken)實現對HLS資料訪問過程的再次加密保護。M3U8標準加密改寫的開啟方式及技術原理請參見配置M3U8標準加密改寫

前提條件

  • 阿里雲視頻加密(HLS 標準加密)屬於免費服務,但需通過視頻轉碼實現加密功能。轉碼操作將產生服務費用,具體計費標準請參考媒資轉碼計費

  • 已經開通了ApsaraVideo for VOD服務。開通步驟請參見開通ApsaraVideo for VOD

  • 已經授權ApsaraVideo for VOD訪問您Key Management Service的許可權,可以通過雲資源訪問授權頁面進行授權。

  • 已在ApsaraVideo for VOD中配置加速網域名稱,具體步驟請參見添加加速網域名稱

  • 已安裝ApsaraVideo for VOD服務端SDK,具體步驟請參見服務端SDK。本文以Java語言為例進行說明。

相關概念

概念

說明

存取控制RAM(Resource Access Management)

是阿里雲提供的系統管理使用者身份與資源存取權限的服務。更多資訊,請參見存取控制

Key Management Service(Key Management Service)

是一站式密鑰管理與資料加密服務平台,提供簡單、可靠、安全、合規的加密保護與憑據管理能力。更多資訊,請參見Key Management Service

資料密鑰DK(Data Key)

資料密鑰是加密資料使用的明文資料密鑰。更多資訊,請參見基本概念

信封資料密鑰EDK(Enveloped Data Key/Encrypted Data Key)

信封資料密鑰是通過信封加密技術保密後的密文資料密鑰。更多資訊,請參見基本概念

加密及解密流程

上傳及加密流程

視頻安全-HLS標準加密3

解密播放流程

開啟M3U8標準加密改寫(推薦)

視頻安全-HLS標準加密6..png

未開啟M3U8標準加密改寫

視頻安全-HLS標準加密2

加密視頻

  1. 上傳視頻並接收回調。

    為確保支援HLS標準加密的視頻不被自動轉碼,在上傳至ApsaraVideo for VOD時需要使用系統內建的不轉碼模板組進行上傳,以防止上傳時自動觸發轉碼。

    1. 通過ApsaraVideo for VOD控制台上傳,請參見通過ApsaraVideo for VOD控制台上傳檔案;通過服務端介面上傳,請參見媒體上傳

    2. 配置ApsaraVideo for VOD的事件通知,當接收到視頻上傳完成的回調訊息時,則表明視頻已經上傳到ApsaraVideo for VOD中。事件通知的配置方法,請參見事件通知

  2. 搭建Data Encryption Service。

    1. 建立Service Key。

      Service Key是Key Management Service中的主加密Key,必須用於產生標準加密金鑰。未建立將導致調用GenerateKMSDataKey - 建立KMS資料密鑰介面產生密鑰時將會報錯。

      1. 單擊頁面左上方的服務地區標識,切換到需要建立Service Key的服務地區下。

        說明

        建立的Service Key與視頻儲存的來源站點地區必須一致,例如:視頻儲存在華東2(上海),則Service Key必須在華東2(上海)建立。

        標準加密-服務地區

      2. 登入ApsaraVideo for VOD控制台,選擇組態管理 > ApsaraVideo for Media Processing配置 > 標準加密

      3. 在標準加密頁面,單擊建立Service Key

        建立成功後,會提示您密鑰(Service Key)建立成功,且在密鑰資訊地區可以查看您的Service Key。

        說明

        若已提示密鑰建立成功,仍未看到Service Key,可能是由於服務角色缺失導致,您可以通過重新授權恢複角色後再重新整理頁面查看Service Key。

    2. 調用介面產生資料密鑰。

      調用GenerateKMSDataKey - 建立KMS資料密鑰介面產生資料密鑰(DK)和信封資料密鑰(EDK)時,無需傳參。成功後,返回的CiphertextBlob即為AES_128格式的(EDK),需緩衝此值用於標準加密轉碼。返回的Plaintext即為(DK)。

  3. 建立用於HLS標準加密的轉碼模板組。

    HLS標準加密過程中需要用到兩個轉碼模板組:系統內建的不轉碼模板組和需要自行建立的HLS標準加密模板組。HLS標準加密模板組建立方法如下:

    1. 登入ApsaraVideo for VOD控制台,選擇組態管理 > ApsaraVideo for Media Processing配置 > 轉碼模板組

    2. 在轉碼模板組頁面建立用於HLS標準加密的模板組。

      其中,封裝格式需設定為hls;在進階參數地區,必須開啟視頻加密,且加密方式勾選私人加密選項;其餘參數您可以根據實際需要進行配置,詳細的參數解釋,請參見轉碼模板視頻安全-HLS加密-控制台

    3. 模板建立完成後,您可以在轉碼模板組列表頁查看轉碼模板組的ID,請儲存該ID用於後續發起HLS標準加密轉碼時使用。視頻安全-HLS加密-控制台1

  4. 發起HLS標準加密轉碼。

    1. 調用SubmitTranscodeJobs介面,發起HLS標準加密轉碼。

      展開查看Java範例程式碼

      下述Java範例程式碼中,您需要按實際情況變更的參數如下:

      參數

      傳入值

      request.setTemplateGroupId("")

      傳入步驟三中建立的用於HLS標準加密的轉碼模板組ID。

      request.setVideoId("")

      傳入步驟一中上傳的待加密的視頻ID。

      encryptConfig.put("CipherText","")

      傳入步驟二中返回的CiphertextBlob值。

      encryptConfig.put("DecryptKeyUri","")

      傳入密鑰URI地址,密鑰URI地址由解密服務地址及CiphertextBlob值拼接而成。以在本地的8099連接埠為例,密鑰URI地址如下:

      http://172.16.0.1:8099?CipherText=CiphertextBlob值

      import com.alibaba.fastjson.JSON;
      import com.alibaba.fastjson.JSONObject;
      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.profile.DefaultProfile;
      import com.aliyuncs.vod.model.v20170321.SubmitTranscodeJobsRequest;
      import com.aliyuncs.vod.model.v20170321.SubmitTranscodeJobsResponse;
      
      public class SubmitTranscodeJobs {
      
          // 阿里雲帳號AccessKey擁有所有API的存取權限,建議您使用RAM使用者進行API訪問或日常營運。
          // 強烈建議不要把AccessKey ID和AccessKey Secret儲存到工程代碼裡,否則可能導致AccessKey泄露,威脅您帳號下所有資源的安全。
          // 本樣本通過從環境變數中讀取AccessKey,來實現API訪問的身分識別驗證。運行程式碼範例前,請配置環境變數ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET。
          private static String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
          private static String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
      
          public static SubmitTranscodeJobsResponse submitTranscodeJobs(DefaultAcsClient client) throws Exception{
              SubmitTranscodeJobsRequest request = new SubmitTranscodeJobsRequest();
              request.setTemplateGroupId("");
              request.setVideoId("");
              JSONObject encryptConfig = new JSONObject();
              encryptConfig.put("CipherText","");
              encryptConfig.put("DecryptKeyUri","");
              encryptConfig.put("KeyServiceType","KMS");
              request.setEncryptConfig(encryptConfig.toJSONString());
              return client.getAcsResponse(request);
          }
      
          public static void main(String[] args) throws ClientException {
              String regionId = "cn-shanghai";  // 點播服務接入地區
              DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
              DefaultAcsClient client = new DefaultAcsClient(profile);
      
              SubmitTranscodeJobsResponse response;
              try {
                  response = submitTranscodeJobs(client);
                  System.out.println("RequestId is:"+response.getRequestId());
                  System.out.println("TranscodeTaskId is:"+response.getTranscodeTaskId());
                  System.out.println("TranscodeJobs is:"+ JSON.toJSON(response.getTranscodeJobs()));
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }       
    2. 如果已經配置了ApsaraVideo for VOD的事件通知,當接收到單個清晰度轉碼完成全部清晰度轉碼完成的回調訊息時,則表明視頻已經轉碼完成。

  5. 查看HLS標準加密結果。

    轉碼完成後,您可以通過如下三種方式來判斷標準加密是否成功。

    • 方式一:登入ApsaraVideo for VOD控制台,選擇媒資庫 > 音/視頻 > 管理 > 視頻地址,在視頻地址頁面,如果視頻有多種格式的輸出(例如還存在格式為mp4的原始檔案),只需要查看m3u8格式的視頻流是否帶有標準加密字樣,如果存在,則表明標準加密成功。視頻安全-HLS標準加密5

    • 方式二:將帶有標準加密字樣的M3U8檔案的地址拷貝出來,使用命令curl -v "M3U8檔案地址",查看擷取到的M3U8內容是否存在URI="<業務方在發起標準加密時傳遞的解密URI地址,即DecryptKeyUri參數值>"關鍵資訊,有則表明為標準加密且加密成功。

    • 方式三:調用GetTranscodeTask - 查詢轉碼任務詳情介面,傳入步驟四中返回參數 JobId 的值。若返回參數TranscodeTemplateId為步驟三中建立的轉碼模板組ID且TranscodeJobStatus為轉碼成功,則表明為標準加密且加密成功。

HLS(M3U8)標準加密改寫(推薦)

開啟M3U8標準加密改寫功能後,系統會在HLS協議的Media Playlist(M3U8檔案)中#EXT-X-KEY標籤後自動添加加密參數(如密碼編譯演算法、密鑰URI及鑒權參數)。用戶端解析改寫後的M3U8檔案時,將使用帶鑒權參數的密鑰URI請求密鑰,通過密鑰和指定演算法解密TS分區,從而實現HLS流媒體的加密訪問保護

步驟一:開啟標準加密參數透傳

在ApsaraVideo for VOD控制台開啟HLS標準加密參數透傳

開啟HLS標準加密參數透傳後,可改寫HLS協議的M3U8檔案,具體為修改#EXT-X-KEY標籤中的URI,在其末尾追加用戶端請求攜帶的參數,預設參數名為MtsHlsUriToken。

前提條件

已配置跨域訪問,詳情請參見配置跨域資源共用

重要

政務雲暫不支援該功能。

操作步驟

  1. 登入點播控制台

  2. 在左側導覽列,單擊組態管理

  3. 單擊分發加速配置 > 網域名稱管理,進入網域名稱管理頁面。

  4. 在指定網域名稱右側的操作列,單擊配置

  5. 在指定網域名稱的左側導覽列,單擊視頻相關

  6. 在加密播放地區,開啟HLS標準加密參數透傳開關p181836

說明
  • 開啟後,HLS標準加密參數透傳時,將通過改寫Token鑒權資訊參數幫您鑒權。改寫的參數名稱為MtsHlsUriToken參數的值為test。當CDN解密播放時,會將MtsHlsUriToken=test追加到M3U8檔案中#EXT-X-KEY標籤的URI末尾。

步驟二:攜帶MtsHlsUriToken參數請求

用戶端攜帶MtsHlsUriToken參數向CDN節點發起對M3U8檔案的訪問請求。

其中,MtsHlsUriToken需要您自行搭建令牌服務,頒發使用者令牌(即產生MtsHlsUriToken)。

下述代碼所產生的Token即是MtsHlsUriToken。下述Java範例程式碼中,您需要按實際情況變更的參數如下:

展開查看Java範例程式碼

參數

傳入值

ENCRYPT_KEY

加密Key,為使用者自訂字串,長度為16、24或32位。

INIT_VECTOR

加密位移量,為使用者自訂字串,長度為16位,不能含有特殊字元。

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;

public class PlayToken {
    //非AES產生方式,無需以下參數
    private static String ENCRYPT_KEY = ""; //加密Key,為使用者自訂的字串,長度為16、24或32位
    private static String INIT_VECTOR = ""; //加密位移量,為使用者自訂字串,長度為16位,不能含有特殊字元

    public static void main(String[] args) throws Exception {

        String serviceId = "12";
        PlayToken playToken = new PlayToken();
        String aesToken = playToken.generateToken(serviceId);
        //System.out.println("aesToken " + aesToken);
        //System.out.println(playToken.validateToken(aesToken));   //驗證解密部分
    }
    /**
     * 根據傳遞的參數產生令牌
     * 說明:
     *  1、參數可以是業務方的使用者ID、播放終端類型等資訊
     *  2、調用令牌介面時產生令牌Token
     * @param args
     * @return
     */
    public String generateToken(String... args) throws Exception {
        if (null == args || args.length <= 0) {
            return null;
        }
        String base = StringUtils.join(Arrays.asList(args), "_");
        //設定30S後,該token到期,到期時間可以自行調整
        long expire = System.currentTimeMillis() + 30000L;
        base += "_" + expire;   //自訂字串,base的長度為16位字元(此例中,時間戳記佔13位,底線(_)佔1位,則還需傳入2位字元。實際配置時也可按需全部更改,最終保證base為16、24或32位字串即可。)
        //產生token
        String token = encrypt(base, ENCRYPT_KEY);  //arg1為要加密的自訂字串,arg2為加密Key
        //儲存token,用於解密時校正token的有效性,例如:到期時間、token的使用次數
        saveToken(token);
        return token;
    }

    /**
     * 驗證token的有效性
     * 說明:
     *  1、解密介面在返回播放密鑰前,需要先校正Token的合法性和有效性
     *  2、強烈建議同時校正Token的到期時間以及Token的有效使用次數
     * @param token
     * @return
     * @throws Exception
     */
    public boolean validateToken(String token) throws Exception {
        if (null == token || "".equals(token)) {
            return false;
        }
        String base = decrypt(token,ENCRYPT_KEY); //arg1為解密字串,arg2為解密Key
        //先校正token的有效時間
        Long expireTime = Long.valueOf(base.substring(base.lastIndexOf("_") + 1));
        System.out.println("時間校正:" + expireTime);
        if (System.currentTimeMillis() > expireTime) {
            return false;
        }
        //從DB擷取token資訊,判斷token的有效性,業務方可自行實現
        TokenInfo dbToken = getToken(token);
        //判斷是否已經使用過該token
        if (dbToken == null || dbToken.useCount > 0) {
            return false;
        }
        //擷取到業務屬性資訊,用於校正
        String businessInfo = base.substring(0, base.lastIndexOf("_"));
        String[] items = businessInfo.split("_");
        //校正商務資訊的合法性,業務方實現
        return validateInfo(items);
    }
    /**
     * 儲存Token到DB
     * 業務方自行實現
     *
     * @param token
     */
    public void saveToken(String token) {
        //TODO 儲存Token
    }
    /**
     * 查詢Token
     * 業務方自行實現
     *
     * @param token
     */
    public TokenInfo getToken(String token) {
        //TODO 從DB擷取Token資訊,用於校正有效性和合法性
        return null;
    }
    /**
     * 校正商務資訊的有效性,業務方可自行實現
     *
     * @param infos
     * @return
     */
    public boolean validateInfo(String... infos) {
        //TODO 校正資訊的有效性,例如UID是否有效等
        return true;
    }
    /**
     * AES加密產生Token
     *
     * @param encryptStr  要加密的字串
     * @param encryptKey  加密Key
     * @return
     * @throws Exception
     */
    public String encrypt(String encryptStr, String encryptKey) throws Exception {
        IvParameterSpec e = new IvParameterSpec(INIT_VECTOR.getBytes("UTF-8"));
        SecretKeySpec skeySpec = new SecretKeySpec(encryptKey.getBytes("UTF-8"), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, e);
        byte[] encrypted = cipher.doFinal(encryptStr.getBytes());
        return Base64.encodeBase64String(encrypted);
    }
    /**
     * AES解密token
     *
     * @param encryptStr  解密字串
     * @param decryptKey  解密Key
     * @return
     * @throws Exception
     */
    public String decrypt(String encryptStr, String decryptKey) throws Exception {

        IvParameterSpec e = new IvParameterSpec(INIT_VECTOR.getBytes("UTF-8"));
        SecretKeySpec skeySpec = new SecretKeySpec(decryptKey.getBytes("UTF-8"), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, e);

        byte[] encryptByte = Base64.decodeBase64(encryptStr);
        byte[] decryptByte = cipher.doFinal(encryptByte);
        return new String(decryptByte);
    }
    /**
     * Token資訊,業務方可提供更多資訊,這裡僅給出樣本供參考
     */
    class TokenInfo {
        //Token的有效使用次數,分布式環境需要注意同步修改問題
        int useCount;
        //token內容
        String token;
    }}
                        

步驟三:解密播放檔案

CDN節點收到用戶端請求後,鑒權通過則解密播放檔案。

若上述步驟二中產生的MtsHlsUriToken參數值為test,則當CDN解密播放時,會將MtsHlsUriToken=test追加到M3U8檔案中#EXT-X-KEY標籤的URI末尾。

具體的鑒權校正邏輯您需要自行實現,可以參考播放視頻中開啟M3U8標準加密改寫方式的解密服務的範例程式碼。

播放視頻

開啟M3U8標準加密改寫(推薦)

  1. 搭建解密服務。

    搭建一個本地HTTP服務,用於解密視頻。

    調用DecryptKMSDataKey - 解密KMS資料密鑰密文介面進行解密,該介面返回的資料密鑰(DK)即PlainTextGenerateKMSDataKey - 建立KMS資料密鑰介面返回的PlainText經過base64_decode之後的資料。

    如果您需要對解密地址進行安全驗證,可以通過開啟M3U8標準加密改寫(預設改寫的參數名為MtsHlsUriToken)實現對HLS資料訪問過程的再次加密保護。根據您是否開啟M3U8標準加密改寫,搭建解密服務的代碼實現不同,具體如下:

    展開查看Java範例程式碼

    下述Java範例程式碼中,您需要按實際情況變更的參數如下:

    參數

    傳入值

    region

    地區標識,要求KMS服務的地區和點播視訊所在的地區保持一致。例如華東2(上海),填寫cn-shanghai,其他地區標識請參見點播地區標識

    AccessKey

    傳入阿里雲帳號或RAM使用者的AccessKey ID和AccessKey Secret,擷取方法請參見建立AccessKey

    httpserver

    根據需求選擇服務啟動的連接埠號碼。

    import com.aliyuncs.DefaultAcsClient;
    import com.aliyuncs.exceptions.ClientException;
    import com.aliyuncs.http.ProtocolType;
    import com.aliyuncs.vod.model.v20170321.DecryptKMSDataKeyRequest;
    import com.aliyuncs.vod.model.v20170321.DecryptKMSDataKeyResponse;
    import com.aliyuncs.profile.DefaultProfile;
    import com.sun.net.httpserver.Headers;
    import com.sun.net.httpserver.HttpExchange;
    import com.sun.net.httpserver.HttpHandler;
    import com.sun.net.httpserver.HttpServer;
    import com.sun.net.httpserver.spi.HttpServerProvider;
    import org.apache.commons.codec.binary.Base64;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.HttpURLConnection;
    import java.net.InetSocketAddress;
    import java.net.URI;import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    public class HlsDecryptServer {
        private static DefaultAcsClient client;
        static {
            //KMS的地區,必須與視頻對應地區
            String region = "";
            // 訪問KMS的授權AccessKey資訊
            // 阿里雲帳號AccessKey擁有所有API的存取權限,建議您使用RAM使用者進行API訪問或日常營運。
            // 強烈建議不要把AccessKey ID和AccessKey Secret儲存到工程代碼裡,否則可能導致AccessKey泄露,威脅您帳號下所有資源的安全。
            // 本樣本通過從環境變數中讀取AccessKey,來實現API訪問的身分識別驗證。運行程式碼範例前,請配置環境變數ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET。
            String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
            String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
            client = new DefaultAcsClient(DefaultProfile.getProfile(region, accessKeyId, accessKeySecret));
        }
        /**
         * 說明:
         * 1、接收解密請求,擷取密文密鑰和使用者令牌Token
         * 2、調用KMS decrypt介面擷取清除金鑰
         * 3、將清除金鑰Base64 decode返回
         */
        public class HlsDecryptHandler implements HttpHandler {
            /**
             * 處理解密請求
             * @param httpExchange
             * @throws IOException
             */
            public void handle(HttpExchange httpExchange) throws IOException {
                String requestMethod = httpExchange.getRequestMethod();
                if ("GET".equalsIgnoreCase(requestMethod)) {
                    //校正token的有效性
                    String token = getMtsHlsUriToken(httpExchange);
                    boolean validRe = validateToken(token);
                    if (!validRe) {
                        return;
                    }
                    //從URL中取得密文密鑰
                    String ciphertext = getCiphertext(httpExchange);
                    if (null == ciphertext)
                        return;
                    //從KMS中解密出來,並Base64 decode
                    byte[] key = decrypt(ciphertext);
                    //設定header
                    setHeader(httpExchange, key);
                    //返回Base64 decode之後的密鑰
                    OutputStream responseBody = httpExchange.getResponseBody();
                    responseBody.write(key);
                    responseBody.close();
                }
            }
            private void setHeader(HttpExchange httpExchange, byte[] key) throws IOException {
                Headers responseHeaders = httpExchange.getResponseHeaders();
                responseHeaders.set("Access-Control-Allow-Origin", "*");
                httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, key.length);
            }
            /**
             * 調用KMS decrypt介面解密,並將明文Base64 decode
             * @param ciphertext
             * @return
             */
            private byte[] decrypt(String ciphertext) {
                DecryptKMSDataKeyRequest request = new DecryptKMSDataKeyRequest();
                request.setCipherText(ciphertext);
                request.setProtocol(ProtocolType.HTTPS);
                try {
                    DecryptKMSDataKeyResponse response = client.getAcsResponse(request);
                    String plaintext = response.getPlaintext();
                    System.out.println("PlainText: " + plaintext);
                    //注意:需要Base64 decode
                    return Base64.decodeBase64(plaintext);
                } catch (ClientException e) {
                    e.printStackTrace();
                    return null;
                }
            }
            /**
             * 從URL中擷取密文密鑰參數
             * @param httpExchange
             * @return
             */
            private String getCiphertext(HttpExchange httpExchange) {
                URI uri = httpExchange.getRequestURI();
                String queryString = uri.getQuery();
                String pattern = "CipherText=(\\w*)";
                Pattern r = Pattern.compile(pattern);
                Matcher m = r.matcher(queryString);
                if (m.find())
                    return m.group(1);
                else {
                    System.out.println("Not Found CipherText Param");
                    return null;
                }
            }
            
              /**
             * 校正令牌有效性 M3U8標準加密改寫必備
             * @param token
             * @return
             */
            private boolean validateToken(String token) {
                if (null == token || "".equals(token)) {
                    return false;
                }
                //TODO 業務方實現令牌有效性校正
                return true;
            }
            /**
             * 擷取Token參數 M3U8標準加密改寫必備
             *
             * @param httpExchange
             * @return
             */
            private String getMtsHlsUriToken(HttpExchange httpExchange) {
                URI uri = httpExchange.getRequestURI();
                String queryString = uri.getQuery();
                String pattern = "MtsHlsUriToken=(\\w*)";
                Pattern r = Pattern.compile(pattern);
                Matcher m = r.matcher(queryString);
                if (m.find())
                    return m.group(1);
                else {
                    System.out.println("Not Found MtsHlsUriToken Param");
                    return null;
                }
            }
        }
        /**
         * 服務啟動
         *
         * @throws IOException
         */
        private void serviceBootStrap() throws IOException {
            HttpServerProvider provider = HttpServerProvider.provider();
            //監聽連接埠可以自訂,能同時接受最多30個請求
            HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8099), 30);
            httpserver.createContext("/", new HlsDecryptHandler());
            httpserver.start();
            System.out.println("hls decrypt server started");
        }
        public static void main(String[] args) throws IOException {
            HlsDecryptServer server = new HlsDecryptServer();
            server.serviceBootStrap();
        }}
  2. 擷取視頻播放地址和憑證。

    調用GetVideoPlayAuth - 擷取音視頻播放憑證介面擷取播放憑證或調用GetPlayInfo - 擷取音視頻播放地址介面擷取播放地址。

  3. 播放加密視頻。

    HLS標準加密支援所有的HLS播放器,您可以選擇使用自研的播放器或阿里雲播放器進行播放加密視頻。

    如果使用阿里雲播放器,請按照阿里雲播放器的要求擷取令牌和鑒權資訊後播放,詳情請參見如何播放加密視頻。如果使用非阿里雲播放器,請自行實現播放邏輯。

    使用阿里雲播放器測試播放的內部流程解析如下:

    開啟M3U8標準加密改寫(推薦)

    流程

    • 播放器解析M3U8檔案中的EXT-X-KEY標籤,擷取解密密鑰URI,該URI對應加密配置 EncryptConfig中的DecryptKeyUri參數值。

    • 為限制非法訪問,播放器需攜帶認證資訊(通過MtsHlsUriToken參數傳遞)請求解密介面。

    • 播放器自動請求解密介面擷取密鑰,解密加密的TS檔案後正常播放。

    樣本

    • 視頻的播放地址為:https://demo.aliyundoc.com/encrypt-stream****-hd.m3u8,則請求時需要攜帶MtsHlsUriToken參數傳入

    • 最終請求地址為:https://demo.aliyundoc.com/encrypt-stream****-hd.m3u8?MtsHlsUriToken=<令牌>

    • 解密地址為:https://demo.aliyundoc.com?CipherText=ZjJmZGViNzUtZWY1Mi00Y2RlLTk3MTMtOT****

    • 最終解密請求地址為:https://demo.aliyundoc.com?CipherText=ZjJmZGViNzUtZWY1Mi00Y2RlLTk3MTMtOT****&MtsHlsUriToken=<頒發的令牌>

未開啟M3U8標準加密改寫

  1. 搭建解密服務。

    搭建一個本地HTTP服務,用於解密視頻。

    調用DecryptKMSDataKey - 解密KMS資料密鑰密文介面進行解密,該介面返回的資料密鑰(DK)即PlainTextGenerateKMSDataKey - 建立KMS資料密鑰介面返回的PlainText經過base64_decode之後的資料。

    具體樣本如下:

    展開查看Java範例程式碼

    下述Java範例程式碼中,您需要按實際情況變更的參數如下:

    參數

    傳入值

    region

    地區標識,要求KMS服務的地區和點播視訊所在的地區保持一致。例如華東2(上海),填寫cn-shanghai,其他地區標識請參見點播地區標識

    AccessKey

    傳入阿里雲帳號或RAM使用者的AccessKey ID和AccessKey Secret,擷取方法請參見建立AccessKey

    httpserver

    根據需求選擇服務啟動的連接埠號碼。

    import com.aliyuncs.DefaultAcsClient;
    import com.aliyuncs.exceptions.ClientException;
    import com.aliyuncs.http.ProtocolType;
    import com.aliyuncs.profile.DefaultProfile;
    import com.aliyuncs.vod.model.v20170321.DecryptKMSDataKeyRequest;
    import com.aliyuncs.vod.model.v20170321.DecryptKMSDataKeyResponse;
    import com.sun.net.httpserver.Headers;
    import com.sun.net.httpserver.HttpExchange;
    import com.sun.net.httpserver.HttpHandler;
    import com.sun.net.httpserver.HttpServer;
    import com.sun.net.httpserver.spi.HttpServerProvider;
    import org.apache.commons.codec.binary.Base64;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.HttpURLConnection;
    import java.net.InetSocketAddress;
    import java.net.URI;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class HlsDecryptServerNoToken {
    
        private static DefaultAcsClient client;
        static {
            //KMS的地區,必須與視頻對應地區
            String region = "cn-beijing";
            //訪問KMS的授權AccessKey資訊
            // 阿里雲帳號AccessKey擁有所有API的存取權限,建議您使用RAM使用者進行API訪問或日常營運。
            // 強烈建議不要把AccessKey ID和AccessKey Secret儲存到工程代碼裡,否則可能導致AccessKey泄露,威脅您帳號下所有資源的安全。
            // 本樣本通過從環境變數中讀取AccessKey,來實現API訪問的身分識別驗證。運行程式碼範例前,請配置環境變數ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET。
            String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
            String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
            client = new DefaultAcsClient(DefaultProfile.getProfile(region, accessKeyId, accessKeySecret));
        }
        /**
         * 說明:
         * 1、接收解密請求,擷取密文密鑰和令牌Token
         * 2、調用KMS decrypt介面擷取清除金鑰
         * 3、將清除金鑰base64decode返回
         */
        public class HlsDecryptHandler implements HttpHandler {
            /**
             * 處理解密請求
             * @param httpExchange
             * @throws IOException
             */
            public void handle(HttpExchange httpExchange) throws IOException {
                String requestMethod = httpExchange.getRequestMethod();
                if ("GET".equalsIgnoreCase(requestMethod)) {
    
                    //從URL中取得密文密鑰
                    String ciphertext = getCiphertext(httpExchange);
                    System.out.println(ciphertext);
                    if (null == ciphertext)
                        return;
                    //從KMS中解密出來,並Base64 decode
                    byte[] key = decrypt(ciphertext);
                    //設定header
                    setHeader(httpExchange, key);
                    //返回base64decode之後的密鑰
                    OutputStream responseBody = httpExchange.getResponseBody();
                    responseBody.write(key);
                    responseBody.close();
                }
            }
            private void setHeader(HttpExchange httpExchange, byte[] key) throws IOException {
                Headers responseHeaders = httpExchange.getResponseHeaders();
                responseHeaders.set("Access-Control-Allow-Origin", "*");
                httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, key.length);
            }
            /**
             * 調用KMS decrypt介面解密,並將明文base64decode
             * @param ciphertext
             * @return
             */
            private byte[] decrypt(String ciphertext) {
    
                DecryptKMSDataKeyRequest request = new DecryptKMSDataKeyRequest();
                request.setCipherText(ciphertext);
                request.setProtocol(ProtocolType.HTTPS);
                try {
                    DecryptKMSDataKeyResponse response = client.getAcsResponse(request);
                    String plaintext = response.getPlaintext();
                    System.out.println("PlainText: " + plaintext);
                    //注意:需要base64 decode
                    return Base64.decodeBase64(plaintext);
                } catch (ClientException e) {
                    e.printStackTrace();
                    return null;
                }
            }
    
            /**
             * 從URL中擷取密文密鑰參數
             * @param httpExchange
             * @return
             */
            private String getCiphertext(HttpExchange httpExchange) {
                URI uri = httpExchange.getRequestURI();
                String queryString = uri.getQuery();
                String pattern = "CipherText=(\\w*)";
                Pattern r = Pattern.compile(pattern);
                Matcher m = r.matcher(queryString);
                if (m.find())
                    return m.group(1);
                else {
                    System.out.println("Not Found CipherText Param");
                    return null;
                }
            }
        }
    
        /**
         * 服務啟動
         *
         * @throws IOException
         */
        private void serviceBootStrap() throws IOException {
            HttpServerProvider provider = HttpServerProvider.provider();
            //監聽連接埠可以自訂,能同時接受最多30個請求
            HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8099), 30);
            httpserver.createContext("/", new HlsDecryptHandler());
            httpserver.start();
            System.out.println("hls decrypt server started");
        }
        public static void main(String[] args) throws IOException {
            HlsDecryptServerNoToken server = new HlsDecryptServerNoToken();
            server.serviceBootStrap();
        }}
    
  2. 擷取視頻播放地址和憑證。

    調用GetVideoPlayAuth - 擷取音視頻播放憑證介面擷取播放憑證或調用GetPlayInfo - 擷取音視頻播放地址介面擷取播放地址。

  3. 播放加密視頻。

    HLS標準加密支援所有的HLS播放器,您可以選擇使用自研的播放器或阿里雲播放器進行播放加密視頻。

    如果使用阿里雲播放器,請按照阿里雲播放器的要求擷取令牌和鑒權資訊後播放,詳情請參見如何播放加密視頻。如果使用非阿里雲播放器,請自行實現播放邏輯。

    使用阿里雲播放器測試播放的內部流程解析如下:

    未開啟M3U8標準加密改寫

    流程

    • 播放器解析M3U8檔案中的EXT-X-KEY標籤,擷取解密密鑰URI,該URI對應加密配置 EncryptConfig中的DecryptKeyUri參數值。

    • 播放器自動請求解密介面擷取密鑰,解密加密的TS檔案後正常播放。

    樣本

    • 視頻的播放地址為:https://demo.aliyundoc.com/encrypt-stream****-hd.m3u8

    • 最終請求地址為:https://demo.aliyundoc.com/encrypt-stream****-hd.m3u8

    • 解密地址為:https://demo.aliyundoc.com?CipherText=ZjJmZGViNzUtZWY1Mi00Y2RlLTk3MTMtOT****

    • 最終解密請求地址為:https://demo.aliyundoc.com?CipherText=ZjJmZGViNzUtZWY1Mi00Y2RlLTk3MTMtOT****

相關文檔