全部產品
Search
文件中心

ApsaraVideo Media Processing:HLS標準加密

更新時間:Feb 26, 2025

視頻加密旨在通過對視頻內容進行深度安全處理,確保視頻資料不被非法擷取和傳播,可有效防止視頻泄露和盜鏈問題,廣泛用於線上教育及財經等對Alibaba Content Security Service性要求高的領域。阿里雲目前支援兩種加密方式:阿里雲私人加密(推薦)和HLS標準加密。本文介紹ApsaraVideo for Media ProcessingHLS加密的原理和接入流程,協助使用者更好的理解和實施HLS加密,在保證視頻安全的同時,實現流暢的線上播放體驗。

工作原理

相關概念

ApsaraVideo for Media Processing採用信封資料加密的方式加密視頻。業務方調用阿里雲Key Management Service(KMS)產生資料密鑰(DK)和信封資料密鑰(EDK),然後利用資料密鑰(DK)加密視頻,並將加密後的檔案和信封資料密鑰(EDK)儲存。播放器終端通過解密服務擷取資料密鑰(DK)請求解密播放視頻。

說明

HLS加密要求使用者自己保護資料密鑰(DK)。

概念

說明

資料密鑰DK(Data Key)

也稱清除金鑰,產生後用於視頻加密。

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

也稱密文密鑰,是通過信封加密技術保密後的密文資料密鑰。主要用於解密資料密鑰,得到明文資料密鑰。

存取控制RAM(Resource Access Management)

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

Key Management Service(Key Management Service)

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

Object Storage Service(Object Storage Service)

阿里雲提供的資料存放區服務,ApsaraVideo for Media Processing操作的媒體資源均存放在OSS的Bucket中。更多資訊,請參見什麼是Object Storage Service

內容分發網路CDN(Content Delivery Network)

阿里雲提供的內容分發網路,在HLS加密流程中,CDN會動態修改M3U8檔案中的解密URI並返回給播放端。更多資訊,請參見什麼是阿里雲CDN

加密流程

ApsaraVideo for Media Processing的視頻加密流程如下:

mts_wf_hls_encrypt

  1. 業務方開通阿里雲ApsaraVideo for Media Processing服務(MPS)、儲存服務(OSS)、存取控制(RAM)、Key Management Service(KMS)以及內容分發網路(CDN)(如未開通)。

  2. 業務方授權ApsaraVideo for Media Processing訪問剛開通的阿里雲Key Management Service(KMS)。

    說明

    授權訪問是為了ApsaraVideo for Media Processing在加密視頻階段調用KMS的GenerateDataKey介面產生資料密鑰(DK)和信封資料密鑰(EDK)。

  3. 業務方配置OSS輸出Bucket網域名稱為CDN加速網域名稱,配置OSS網域名稱CNAME及回源Host。

  4. 業務方建立視頻加密工作流程,傳入OSS輸出Bucket、Key URI等關鍵資訊。

    Key URI是業務方的服務地址。ApsaraVideo for Media Processing完成視頻加密後的M3U8檔案中會包含Key URI資訊。

  5. 業務方上傳需要加密的視頻,上傳過程中指定建立好的視頻加密工作流程。

  6. 視頻上傳完成後ApsaraVideo for Media Processing自動觸發加密轉碼。

    ApsaraVideo for Media Processing調用GenerateDataKey介面產生資料密鑰(DK)和信封資料密鑰(EDK),並使用資料密鑰(DK)加密視頻。加密完成後,將業務方提供的Key URI與信封資料密鑰(EDK)寫入M3U8檔案。

  7. ApsaraVideo for Media Processing將M3U8檔案及ts檔案存入OSS的輸出Bucket中。

解密流程

播放端解密播放HLS加密視頻的流程如下:

mts_hls_decrypt

  1. 業務方搭建令牌服務,用於頒發令牌MtsHlsUriToken。

    重要

    令牌服務指用於派發MtsHlsUriToken的服務。

  2. 業務方調用KMS解密介面搭建解密服務,用於解密視頻,同時提供資料密鑰(DK)給播放終端。

    重要

    KMS返回Base64加密後的資料密鑰給業務方。業務方需要將調用KMS介面獲得的資料密鑰Base64 Decode之後返回給播放終端。

  3. 業務方調用MPS的QueryMediaList介面擷取視頻M3U8檔案的OSS地址,並將地址拼接MtsHlsUriToken後返回給播放終端。

  4. 播放終端攜帶MtsHlsUriToken、資料密鑰向阿里雲CDN請求播放地址,阿里雲CDN改寫M3U8檔案,將業務方的Key URI與信封加密金鑰返回播放終端。播放終端解密播放視頻。

業務方實現概覽

在完整的HLS加密視頻和解密播放流程中,需要業務方自行實現的代碼邏輯包括:

  1. 建立加密工作流程。

    說明

    儘管控制台也可以建立加密工作流程,但為了更完整高效地使用HLS標準Data Encryption Service,建議您整合服務端SDK後建立。

  2. 搭建頒發及驗證MtsHlsUriToken令牌服務,並校正解密令牌。推薦一個令牌只允許使用一次。

  3. 調用KMS服務的解密介面搭建解密服務,並將調用KMS介面獲得的清除金鑰Base64 Decode之後返回播放終端。

前提條件

接入ApsaraVideo for Media Processing的HLS加密功能需要完成以下準備:

  1. 開通相關阿里雲服務。

    如果您尚未開通MPS,OSS,KMS, RAM,CDN服務,請先開通服務。

    1. 開通MPS服務。具體操作,請參見開通MPS服務

    2. 開通OSS服務。具體操作,請參見開通OSS服務

    3. 開通KMS服務。具體操作,請參見開通KMS服務

    4. 開通CDN服務。具體操作,請參見開通CDN服務

  2. 授權ApsaraVideo for Media Processing訪問KMS。

    1. 登入RAM存取控制台

    2. 單擊授权進入新增授權頁面。

    3. 授權主體搜尋方塊搜尋AliyunMtsDefaultRole,選擇系統建立的可供ApsaraVideo for Media Processing使用的角色。

    4. 權限原則下方的搜尋方塊搜尋KMS並選擇AliyunKMSFullAccess,然後單擊確認新增授權

    完成授權後,ApsaraVideo for Media Processing可以訪問您的KMS服務。收到加密視頻請求時,ApsaraVideo for Media Processing調取KMS介面擷取資料祕密金鑰加密視頻。

  3. 配置OSS輸出Bucket網域名稱及回源Host。具體操作,請參見配置加速網域名稱。如已配置請忽略。

    說明

    OSS網域名稱可以手動輸入阿里雲OSS Bucket的外網網域名稱(OSS外網網域名稱可前往OSS控制台查看),如:exampleBucket****.oss-cn-hangzhou.aliyuncs.com ,也可以直接選擇同帳號下需要加速的OSS Bucket。不支援OSS內網網域名稱。

加密視頻

請按以下指引完成視頻加密:

  1. 建立加密工作流程。

    建立加密工作流程需要整合阿里雲SDK並添加ApsaraVideo for Media Processing相關依賴。請根據您使用的開發語言選擇查看對應程式碼範例。

    重要

    建立加密工作流程時必須傳入業務方的Key URI地址,ApsaraVideo for Media Processing加密視頻時將該地址寫入M3U8檔案儲存體到OSS。Key URI樣本:example.aliyundoc.com

    語言

    整合SDK

    加密工作流程程式碼範例

    Java

    Java SDK安裝

    建立HLS標準加密工作流程

    Python

    Python SDK安裝

    建立HLS標準加密工作流程

    PHP

    PHP SDK安裝

    建立HLS標準加密工作流程

  2. 上傳視頻觸發加密轉碼。您可以通過ApsaraVideo for Media Processing控制台或OSS控制台上傳視頻。

    說明

    由於加密工作流程已經建立,上傳視頻時指定建立好的加密工作流程即可自動觸發ApsaraVideo for Media Processing加密轉碼。

    加密完成後,可登入OSS控制台,在輸出Bucket檔案路徑下查看M3U8檔案。檔案樣本如下:

    #EXTM3U
         #EXT-X-VERSION:3
         #EXT-X-TARGETDURATION:5
         #EXT-X-MEDIA-SEQUENCE:0
         #EXT-X-KEY:METHOD=AES-128,URI="https://example.aliyundoc.com?Ciphertext=aabbccddeeff&MediaId=fbbf98691ea44b7c82dd75c5bc8b****"
         #EXTINF:4.127544,
         15029611683170-00001.ts
         #EXT-X-ENDLIST

    樣本中,業務方配置的Key URI與ApsaraVideo for Media Processing從KMS擷取的信封資料密鑰(EDK)包含在URI中。

播放HLS加密視頻

請按以下指引完成視頻解密播放:

  1. 搭建令牌服務。

    說明

    令牌服務需要根據您的加密邏輯自行搭建,以達到更高的視頻安全等級。

  2. 搭建解密服務。

    搭建一個本地HTTP服務,用於解密視頻和擷取解密密鑰。ApsaraVideo for Media Processing提供Java及Python程式碼範例。

    • Java程式碼範例

      Java SDK需要的依賴如下:

      解密樣本Base64:

      展開查看範例程式碼

      import com.sun.net.httpserver.*;
      import com.sun.net.httpserver.spi.HttpServerProvider;
      
      import java.io.IOException;
      import java.io.OutputStream;
      import java.net.HttpURLConnection;
      import java.net.InetSocketAddress;
      
      /**
       * *****   使用須知     ******
       * 本demo Base64方式解密服務程式碼範例
       * demo中連接埠號碼為8888, 請注意與KeyUri解密服務連接埠保持一致
       * 如果您需要額外的Token令牌校正, 請參考kms方式解密服務的實現邏輯
       *
       * *****   邏輯概述     ******
       * 1、接收解密請求,擷取密文密鑰
       * 2、將清除金鑰base64decode返回
       */
      public class Base64DecryptServer {
      
          public static void main(String[] args) throws IOException {
              Base64DecryptServer server = new Base64DecryptServer();
              server.startService();
          }
      
          public class Base64DecryptHandler implements HttpHandler {
              /**
               * 處理解密請求
               * @param httpExchange
               * @throws IOException
               */
              public void handle(HttpExchange httpExchange) throws IOException {
                  String requestMethod = httpExchange.getRequestMethod();
                  if ("GET".equalsIgnoreCase(requestMethod)) {
                      // 此處的解密密鑰需要和加密時候的密鑰一致
                      byte[] key = "encryptionkey128".getBytes();
                      // 設定header
                      setHeader(httpExchange, key);
                      // 返回base64decode之後的密鑰
                      OutputStream responseBody = httpExchange.getResponseBody();
                      System.out.println(new String(key));
                      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);
              }
          }
          /**
           * 服務啟動
           * @throws IOException
           */
          private void startService() throws IOException {
              HttpServerProvider provider = HttpServerProvider.provider();
              //監聽連接埠9999,能同時接受30個請求
              HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8888), 30);
              httpserver.createContext("/", new Base64DecryptHandler());
              httpserver.start();
              System.out.println("base64 hls decrypt server started");
          }
      
      }

      解密樣本KMS:

      展開查看範例程式碼

      import com.aliyun.mps.sdk.utils.InitClient;
      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.http.ProtocolType;
      import com.aliyuncs.kms.model.v20160120.DecryptRequest;
      import com.aliyuncs.kms.model.v20160120.DecryptResponse;
      import com.sun.net.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;
      
      /**
       * *****   使用須知     ******
       * 本demo 為未開啟標準加密改寫時的kms方式解密服務程式碼範例, 不包含MtsHlsUriToken的校正邏輯
       * demo中連接埠號碼為8888, 請注意與KeyUri解密服務連接埠保持一致
       *
       * *****   邏輯概述     ******
       * 1、接收解密請求,擷取密文密鑰
       * 2、調用KMS decrypt介面通過Ciphertext擷取清除金鑰 
       * 3、將清除金鑰base64decode返回
       */
      public class HlsDecryptServerNoToken {
      
          private static DefaultAcsClient client;
          static {
              try{
                  client = InitClient.initMpsClient();
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      
          public static void main(String[] args) throws IOException {
              HlsDecryptServerNoToken server = new HlsDecryptServerNoToken();
              server.startService();
          }
      
          public class HlsDecryptHandler implements HttpHandler {
              public void handle(HttpExchange httpExchange) throws IOException {
                  String requestMethod = httpExchange.getRequestMethod();
                  if(requestMethod.equalsIgnoreCase("GET")){
                      //從URL中取得密文密鑰
                      String ciphertext = getCiphertext(httpExchange);
                      System.out.println(ciphertext);
                      if (null == ciphertext)
                          return;
                      //從KMS中解密出來,並Base64 decode
                      byte[] key = decrypt(ciphertext);
                      //設定header
                      setHeader(httpExchange, key);
                      //返回密鑰
                      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);
              }
      
              private byte[] decrypt(String ciphertext) {
                  DecryptRequest request = new DecryptRequest();
                  request.setCiphertextBlob(ciphertext);
                  request.setProtocol(ProtocolType.HTTPS);
                  try {
                      DecryptResponse response = client.getAcsResponse(request);
                      String plaintext = response.getPlaintext();
                      //注意:需要base64 decode
                      return Base64.decodeBase64(plaintext);
                  } catch (ClientException e) {
                      e.printStackTrace();
                      return null;
                  }
              }
              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");
                      return null;
                  }
              }
          }
      
          /**
           * 服務啟動
           *
           * @throws IOException
           */
          private void startService() throws IOException {
              HttpServerProvider provider = HttpServerProvider.provider();
              //監聽連接埠8888,能同時接受30個請求, 可自行更改連接埠
              HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8888), 30);
              httpserver.createContext("/", new HlsDecryptHandler());
              httpserver.start();
              System.out.println("no token hls decrypt server started");
          }
      
      }
    • Python程式碼範例

      Python SDK需要的依賴如下:

      • pip install aliyun-python-sdk-core

      • pip install aliyun-python-sdk-kms

      • pip install aliyun-python-sdk-mts

      Python程式碼範例如下:

      展開查看範例程式碼

      # -*- coding: UTF-8 -*-
      from http.server import BaseHTTPRequestHandler, HTTPServer
      from urllib.parse import urlparse, parse_qs
      from aliyunsdkcore.client import AcsClient
      from aliyunsdkcore.request import CommonRequest
      import cgi
      import json
      import base64
      import logging
      from urllib.parse import urlparse
      
      logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
      #please replace access_key_id, access_secret, region_id
      client = AcsClient("","")
      
      class AuthorizationHandler(BaseHTTPRequestHandler):
        def do_GET(self):
          #self.check()
          self.set_header()
          cipertext = self.get_cihpertext()
          #print(cipertext)
          plaintext = self.decrypt_cihpertext(cipertext)
          #print(plaintext)
          key = base64.b64decode(plaintext)
          #print(key)
          self.wfile.write(key)
      
        def do_POST(self):
          pass
        
        def check(self):
      #check MtsHlsUriToken, etc.
          pass
        
        def set_header(self):
          self.send_response(200)
      #cors
          self.send_header('Access-Control-Allow-Origin', '*')
          self.end_headers()
      
        def get_cihpertext(self):
          path = urlparse(self.path)
          query = parse_qs(path.query) 
          return query.get('Ciphertext')[0]
          
        def decrypt_cihpertext(self, cipertext):
          request = CommonRequest()
          request.set_domain('')
          request.set_version('2014-06-18')
          request.set_action_name('DecryptKMSDataKey')
          request.add_query_param('CipherText', cipertext)
      
          response = client.do_action_with_exception(request)
          # print("####################################")
          # print(response)
          jsonResp = json.loads(response)
          return jsonResp["PlainText"]
        
      if __name__ == '__main__':
      # Start a simple server, and loop forever
        print("Starting server, use  to stop")
        server = HTTPServer(('127.0.0.1', 8099), AuthorizationHandler)
        server.serve_forever()
  3. 調用ApsaraVideo for Media Processing的查詢媒體-使用媒體ID介面擷取播放地址。

    您可以通過開發人員門戶直接調用API或將API整合到服務中調用。

  4. 播放加密視頻。

    您可以使用自己的播放器或阿里雲播放器播放加密視頻。

    • 如果使用非阿里雲播放器,請自行實現播放邏輯。

    • 如果使用阿里雲播放器,請按照阿里雲播放器的要求擷取令牌和鑒權資訊後播放。詳細介紹請參見視頻播放

    您也可以藉助一個線上播放器,測試HLS加密視頻的播放。

    阿里雲播放器使用者診斷工具為例,請將擷取的播放地址,填入對話方塊中,單擊視頻播放

    說明

    通過瀏覽器DEBUG,可以看到播放器自動請求了鑒權伺服器,擷取解密密鑰,並進行解密播放。

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

    • 擷取播放地址後,播放器把OSS網域名稱替換為CDN網域名稱,再拼接上參數MtsHlsUriToken作為請求解密密鑰的令牌向CDN獲請求播放地址。請求樣本:https://example.aliyundoc.com/test_01.m3u8?MediaId=fbbf98691ea44b7c82dd75c5bc8b****&MtsHlsUriToken=<業務方頒發的令牌>

      重要

      如果您使用的是阿里雲播放器,則無需自行拼接MtsHlsUriToken。如果您使用的是其它播放器,則需要自行拼接。

    • 收到請求後,阿里雲CDN動態修改M3U8檔案中的解密URI並返回修改後的地址給播放器,如原為https://example.aliyundoc.com?Ciphertext=aabbccddeeff&MediaId=fbbf98691ea44b7c82dd75c5bc8b****,修改後為 https://example.aliyundoc.com?Ciphertext=aabbccddeeff&MediaId=fbbf98691ea44b7c82dd75c5bc8b****&MtsHlsUriToken=<業務方頒發的令牌>

    • 播放器訪問M3U8檔案中EXT-X-KEY標籤中的URI以擷取解密密鑰,此URI為業務方搭建的解密密鑰介面。業務方調用KMS的Decrypt介面,並將擷取的清除金鑰Base64decode之後返回給播放器。播放器用資料密鑰(DK)去解密加密過的ts檔案進行播放。

相關文檔

阿里雲私人加密