すべてのプロダクト
Search
ドキュメントセンター

ApsaraVideo VOD:HLS 暗号化

最終更新日:Jun 09, 2025

HLS 暗号化は、AES-128 を使用して動画を暗号化します。 このメソッドは、すべての HLS 互換プレーヤーで動作し、アクセス制御のためにキー管理とトークンサービスが必要です。 HLS 暗号化は、オンライン教育プラットフォームや限定コンテンツストリーミングなどのセキュリティ要求の高い分野で一般的に使用されます。 このトピックでは、動画配信の HLS 暗号化と動画再生の復号化を実装する方法について説明します。

動作原理

ApsaraVideo VOD はエンベロープ暗号化技術を採用しています。この技術では、サービスプロバイダーは Alibaba Cloud Key Management Service(KMS)を介してデータキー(DK)とエンベロープデータキー(EDK)を生成します。 DK は動画コンテンツを暗号化し、EDK は暗号化された動画と一緒に保存されます。 再生中、復号化サービスは DK を取得して動画を復号化します。

復号化 URL を検証する場合は、M3U8 暗号化と書き換えを有効にできます(デフォルトでは、MtsHlsUriToken が書き換えられます)。 これにより、HLS データアクセスに暗号化の層が追加されます。

前提条件

  • Alibaba Cloud アカウントに残高が十分にあること。 HLS 暗号化は無料のサービスですが、暗号化には動画のトランスコードが必要なため、トランスコード操作にはサービス料金が発生します。 料金の詳細については、「メディアトランスコーディング」をご参照ください。

  • ApsaraVideo VOD がアクティブ化されていること。 詳細については、「ApsaraVideo VOD のアクティブ化」をご参照ください。

  • ApsaraVideo VOD には KMS にアクセスする権限が付与されています。 クラウド リソースアクセス承認 ページで、ApsaraVideo VOD に KMS へのアクセスを承認できます。

  • 高速化ドメインが ApsaraVideo VOD に追加されていること。 詳細については、「CDN 用のドメイン名を追加する」をご参照ください。

  • ApsaraVideo VOD SDK が統合されています。このトピックでは、例として Java 用 ApsaraVideo VOD SDK を使用します。

概念

概念

説明

RAM

Resource Access Management(RAM)は、ユーザー ID を管理し、リソースへのアクセスを制御するのに役立つ Alibaba Cloud のサービスです。 詳細については、「RAM とは」をご参照ください。

KMS

KMS は、キー管理、データ暗号化、およびシークレット管理のための包括的なプラットフォームです。 データが暗号化され、保護され、業界標準に準拠していることを保証する、シンプルで信頼性が高く、安全なソリューションを提供します。 詳細については、「KMS とは」をご参照ください。

DK

DK は、データを暗号化するために使用されるプレーンテキストキーです。 詳細については、「用語」をご参照ください。

EDK

EDK は、エンベロープ暗号化を使用して作成された暗号文データキーです。 詳細については、「用語」をご参照ください。

暗号化と復号のプロセス

アップロードと暗号化

视频安全-HLS标准加密3

復号と再生

(推奨) M3U8 暗号化と書き換えが有効になっている場合

视频安全-HLS标准加密6..png

M3U8 暗号化と書き換えが無効になっている場合

视频安全-HLS标准加密2

動画を暗号化する

  1. 動画をアップロードして、コールバックを受信します。

    HLS 暗号化をサポートする動画が自動的にトランスコードされないようにするには、ApsaraVideo VOD にアップロードするときに、システム組み込みの [トランスコードなし] テンプレートグループを使用する必要があります。 これにより、アップロードプロセス中に自動トランスコードがトリガーされるのを防ぎます。

    1. ApsaraVideo VOD コンソール または API を使用して動画をアップロードします。

    2. イベント通知 を設定します。 FileUploadComplete コールバックメッセージを受信すると、動画が ApsaraVideo VOD に正常にアップロードされたことを意味します。

  2. キー管理サービスを設定します。

    1. サービスキーを作成します。

      サービスキーは KMS の主要な暗号化キーであり、HLS 暗号化キーの生成に使用される必要があります。 サービスキーを作成しないと、GenerateKMSDataKey 操作を呼び出すときにエラーが発生します。

      1. 詳細については、「App Service へのコードのデプロイ」をご参照ください。

        説明

        サービスキーは、動画が保存されているオリジンサーバーと同じリージョンで作成する必要があります。たとえば、動画が中国 (上海) に保存されている場合、サービスキーも中国 (上海) で作成する必要があります。

        标准加密-服务地域

      2. ApsaraVideo VOD コンソール にログインします。 左側のナビゲーションウィンドウで、[構成管理] > [メディア処理] > [HLS 暗号化] を選択します。

      3. 手順 2[HLS 暗号化] ページで、 をクリックします。サービスキーが作成されると、システムは というメッセージを表示します。 セクションでサービスキーを表示できます。

        サービスキーが作成されると、システムによって [サービスキーが作成されました。] と表示されます。 [キー情報] セクションでサービスキーを表示できます。

        説明

        サービスキーが正常に作成されたという通知を受け取ったにもかかわらず、キーが表示されない場合は、サービスロールが不足していることが原因である可能性があります。再認証を実行してロールを復元し、ページを更新してサービスキーを表示します。

    2. DK を生成します。

      DK と EDK を生成するには、入力パラメーターなしで GenerateKMSDataKey 操作を呼び出します。 成功すると、レスポンスの CiphertextBlob は HLS 暗号化とトランスコード用にキャッシュする必要がある AES_128 形式の EDK を提供し、レスポンスの Plaintext には DK が含まれます。

  3. HLS 暗号化用のトランスコーディングテンプレートグループを作成します。

    HLS 暗号化プロセス中に、組み込みの [トランスコードなし] テンプレートグループとカスタムトランスコーディングテンプレートグループの 2 つのトランスコーディングテンプレートグループが必要です。 HLS 暗号化用のカスタムトランスコーディングテンプレートグループを作成する方法は次のとおりです。

    1. ApsaraVideo VOD コンソール にログインします。 左側のナビゲーションウィンドウで、[構成管理] > [メディア処理] > [トランスコーディングテンプレートグループ] を選択します。

    2. ページで、 をクリックします。 [基本パラメーター] セクションで、 を に設定します。 セクションで、 をオンにし、 を [Alibaba Cloud 独自の暗号化]WordPress の基本的な に設定します。 その他のパラメーターについては、実際のニーズに基づいて設定します。 パラメーターの説明については、「」をご参照ください。

      [基本パラメーター] セクションで、[カプセル化フォーマット]hls に設定します。[詳細パラメーター] セクションで、[ビデオの暗号化] をオンにし、[暗号化方式]Alibaba Cloud 独自暗号化 に設定します。その他のパラメーターについては、実際のニーズに基づいて設定してください。パラメーターの説明については、「トランスコーディング テンプレート」をご参照ください。视频安全-HLS加密-控制台

    3. WordPressトランスコーディングテンプレートグループを作成した後、 ページでその ID を見つけます。 この ID は後で HLS 暗号化とトランスコードに必要になるため、保存してください。视频安全-HLS加密-控制台1

  4. HLS 暗号化とトランスコードのリクエストを開始します。

    1. SubmitTranscodeJobs 操作を呼び出して、HLS 暗号化とトランスコードを開始します。

      Java のサンプルコード

      次の表は、サンプルコードのパラメーターについて説明しています。 ビジネスニーズに基づいて変更できます。

      パラメーター

      説明

      request.setTemplateGroupId("")

      手順 3 で作成したトランスコーディングテンプレートグループの ID。

      request.setVideoId("")

      手順 1 でアップロードした動画の ID。

      encryptConfig.put("CipherText","")

      手順 2 で返された CiphertextBlob の値。

      encryptConfig.put("DecryptKeyUri","")

      キー URI。 URI は、復号化サービスの IP アドレスと 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;
      
      // Alibaba Cloud アカウントの AccessKey ペアには、すべての API 操作を呼び出す権限があります。 API 操作の呼び出しや日常的な O&M の実行には、RAM ユーザーの AccessKey ペアを使用することをお勧めします。
      // AccessKey ペア(AccessKey ID と AccessKey シークレット)をプロジェクトコードにハードコードしないことをお勧めします。 そうしないと、AccessKey ペアが漏洩し、アカウント内のすべてのリソースのセキュリティが侵害される可能性があります。
      // この例では、ApsaraVideo VOD は環境変数から AccessKey ペアを読み取って、API アクセスの ID 検証を実装します。 サンプルコードを実行する前に、次の環境変数を設定します。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(""); // トランスコーディングテンプレート ID を設定します。
          request.setVideoId(""); // 動画 ID を設定します。
          JSONObject encryptConfig = new JSONObject();
          encryptConfig.put("CipherText",""); // 暗号文を設定します。
          encryptConfig.put("DecryptKeyUri",""); // 復号化キーの URI を設定します。
          encryptConfig.put("KeyServiceType","KMS");
          request.setEncryptConfig(encryptConfig.toJSONString());
          return client.getAcsResponse(request);
      }
      
      public static void main(String[] args) throws ClientException {
          String regionId = "cn-shanghai";  // ApsaraVideo VOD がアクティブ化されているリージョンを指定します。
          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 VOD でイベント通知を設定している場合、StreamTranscodeComplete または TranscodeComplete コールバックのいずれかを受信すると、動画のトランスコードが完了します。

  5. HLS 暗号化の結果を表示します。

    トランスコードが完了した後、次の方法を使用して HLS 暗号化が成功したかどうかを判断できます。

    • 方法 1: ApsaraVideo VOD コンソール にログオンします。左側のナビゲーションウィンドウで、[メディアファイル] > [オーディオ/ビデオ] を選択します。 [ビデオとオーディオ] ページで、アップロードしたビデオを見つけ、[操作] 列の [管理] をクリックします。 [ビデオ URL] タブで、MP4 形式以外の形式のビデオ出力が存在し、[HLS 暗号化] が M3U8 ファイルにタグ付けされている場合、HLS 暗号化は成功です。视频安全-HLS标准加密5

    • 方法 2:[HLS 暗号化] のタグが付いている M3U8 ファイルの URL をコピーし、curl -v "M3U8 ファイルの URL" コマンドを実行して、結果を確認します。 HLS 暗号化を開始するときに指定した復号化 URL が DecryptKeyUri の値と同じ 場合、HLS 暗号化は成功しています。

    • 方法 3:GetTranscodeTask 操作を呼び出し、手順 4 で返された JobId パラメーターの値を渡します。 レスポンスの TranscodeTemplateId の値が 手順 3 で作成したトランスコーディングテンプレートグループの ID と一致し、TranscodeJobStatus が「トランスコード成功」と表示されている場合、HLS 暗号化は成功しています。

(推奨)M3U8 暗号化と書き換え

M3U8 暗号化と書き換え機能を有効にすると、システムは M3U8 プレイリストファイルの #EXT-X-KEY タグの後に暗号化パラメーター(暗号化アルゴリズム、キー URI、認証パラメーターなど)を自動的に追加します。 クライアントは、書き換えられた M3U8 プレイリストファイルを処理するときに、キー URI(認証パラメーターを含む)を使用して復号化キーをリクエストします。 次に、クライアントはこのキーと指定されたアルゴリズムを使用してトランスポートストリーム(TS)ファイルを復号化し、HLS ストリームへの暗号化されたアクセスを保証します。

手順 1:HLS 暗号化のパラメーターパススルーを有効にする

ApsaraVideo VOD コンソールにログインし、[HLS 暗号化のパラメーターパススルー] をオンにします。

[HLS 暗号化のパラメーターパススルー] を有効にすると、M3U8 プレイリストファイルを変更できます。 具体的には、クライアントリクエストで渡されたパラメーターを最後に追加することにより、#EXT-X-KEY タグの URI を変更します。 デフォルトでは、MtsHlsUriToken が書き換えられます。

前提条件

クロスオリジン リソース共有(CORS) が設定されていること。

重要

Alibaba Cloud GovCloud は、HLS 暗号化のパラメーターパススルーをサポートしていません。

手順

  1. ApsaraVideo VOD コンソール にログインします。

  2. 左側のナビゲーションウィンドウで、[構成管理] を見つけます。

  3. [CDN 構成] > [ドメイン名] を選択します。 [ドメイン名] ページが表示されます。

  4. 設定するドメイン名を見つけて、[アクション] 列の [構成] をクリックします。

  5. ドメイン名の左側のナビゲーションウィンドウで、[動画関連] をクリックします。

  6. [暗号化再生] セクションで、[HLS 暗号化のパラメーターパススルー] をオンにします。p181836

説明
  • この機能を有効にすると、HLS 暗号化パラメーターを渡すときに、Token パラメーターを書き換えることで、システムが自動的に認証を処理します。 書き換えられたパラメーターは MtsHlsUriToken で、その値は test です。 CDN の復号化と再生中に、MtsHlsUriToken=test が M3U8 プレイリストファイルの #EXT-X-KEY タグの URI の最後に追加されます。

手順 2:MtsHlsUriToken パラメーターを含むリクエストを送信する

プレゼンスポイント(POP)にリクエストを送信して M3U8 プレイリストファイルにアクセスし、リクエストに MtsHlsUriToken を含めます。

MtsHlsUriToken を取得するには、トークンサービスを設定する必要があります。

次のサンプルコードは、MtsHlsUriToken を取得する方法を示しています。 次の表は、サンプルコードのパラメーターについて説明しています。 ビジネスニーズに基づいて変更できます。

Java のサンプルコード

パラメーター

説明

ENCRYPT_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 = ""; // カスタム暗号鍵。 文字列の長さは 16、24、または 32 文字である必要があります。
    private static String INIT_VECTOR = ""; // カスタム暗号化オフセット。 文字列の長さは 16 文字である必要があり、特殊文字を含めることはできません。

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

        String serviceId = "12"; // サービス ID を設定します。
        PlayToken playToken = new PlayToken();
        String aesToken = playToken.generateToken(serviceId);
        //System.out.println("aesToken " + aesToken);
        //System.out.println(playToken.validateToken(aesToken));   // トークンを確認します。
    }
    /**
     * 設定されたパラメーターに基づいてトークンを生成します。
     * 注:
     * 1. パラメーターには、ユーザー ID と再生デバイスのタイプが含まれます。
     * 2. トークン操作が呼び出されると、トークンが生成されます。
     * @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), "_");
        // トークンの有効期間を 30 秒に設定します。 ビジネス要件に基づいて値を変更できます。
        long expire = System.currentTimeMillis() + 30000L;
        base += "_" + expire;   // 長さが 16 文字のカスタム文字列。 この例では、タイムスタンプに 13 文字が含まれ、アンダースコア(_)の長さが 1 文字であるため、さらに 2 文字が必要です。 base パラメーターの値を変更できます。 値の長さは 16、24、または 32 文字である必要があります。
        // トークンを生成します。
        String token = encrypt(base, ENCRYPT_KEY);  // arg1 は暗号化するカスタム文字列です。 arg2 は暗号鍵です。
        // トークンを保存します。 トークンの有効性は、有効期間やトークンが使用された回数など、復号化中にチェックされます。
        saveToken(token);
        return token;
    }

    /**
     * トークンが有効かどうかを確認します。
     * 注:
     * 1. 復号化操作が再生キーを返す前に、復号化サービスはトークンが正当で有効かどうかを確認します。
     * 2. トークンの有効期間とトークンが使用された回数をチェックすることをお勧めします。
     * @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 は復号鍵です。
        // トークンの有効期間を確認します。
        Long expireTime = Long.valueOf(base.substring(base.lastIndexOf("_") + 1));
        System.out.println("有効期間を確認します:" + expireTime);
        if (System.currentTimeMillis() > expireTime) {
            return false;
        }
        // データベースからトークン情報を取得し、トークンが有効かどうかを確認します。 ロジックをカスタマイズできます。
        TokenInfo dbToken = getToken(token);
        // トークンが使用されているかどうかを確認します。
        if (dbToken == null || dbToken.useCount > 0) {
            return false;
        }
        // 確認のためのビジネス属性を取得します。
        String businessInfo = base.substring(0, base.lastIndexOf("_"));
        String[] items = businessInfo.split("_");
        // ビジネス属性の有効性を確認します。 ロジックをカスタマイズできます。
        return validateInfo(items);
    }
    /**
     * トークンをデータベースに保存します。
     * ロジックをカスタマイズできます。
     *
     * @param token
     */
    public void saveToken(String token) {
        // TODO。 トークンを保存します。
    }
    /**
     * トークンをクエリします。
     * ロジックをカスタマイズできます。
     *
     * @param token
     */
    public TokenInfo getToken(String token) {
        // TODO。 データベースからトークンを取得し、トークンが有効かどうかを確認します。
        return null;
    }
    /**
     * ビジネス属性の有効性を確認します。 ロジックをカスタマイズできます。
     *
     * @param infos
     * @return
     */
    public boolean validateInfo(String... infos) {
        // TODO。 UID などの情報の有効性を確認します。
        return true;
    }
    /**
     * AES アルゴリズムを使用してトークンを生成します。
     *
     * @param encryptStr  暗号化する文字列。
     * @param encryptKey  暗号鍵。
     * @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 アルゴリズムを使用してトークンを復号化します。
     *
     * @param encryptStr  復号化する文字列。
     * @param decryptKey  復号鍵。
     * @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);
    }
    /**
     * トークン情報を取得します。 サンプルコードは参照用です。 ビジネスシナリオに基づいて追加のトークン情報を取得できます。
     */
    class TokenInfo {
        // トークンが使用された回数を取得します。 分散環境では、変更を同期する必要があります。
        int useCount;
        // トークンの内容。
        String token;
    }}
                        

手順 3:再生ファイルを復号化する

クライアントリクエストを受信した後、POP は認証に成功すると再生ファイルを復号化します。

手順 2 で生成された MtsHlsUriToken の値が test の場合、Alibaba Cloud CDN は復号化と再生中に MtsHlsUriToken=test を M3U8 プレイリストファイルの #EXT-X-KEY タグの URI の最後に追加します。

認証検証ロジックを自分で実装する必要があります。 参考のために、「動画を再生する」セクションで、M3U8 暗号化と書き換えが有効になっている復号化サービスを設定するためのサンプルコードを確認できます。

動画を再生する

(推奨)M3U8 暗号化と書き換えが有効になっている場合

  1. 復号化サービスを設定します。

    動画を復号化するためのローカル HTTP サービスを設定します。

    DecryptKMSDataKey 操作を呼び出して DK を復号化します。 レスポンスの PlainText 値には DK が含まれています。 この DK は、GenerateKMSDataKey 操作へのレスポンスで返された PlainText パラメーターの Base64 デコードバージョンです。

    復号化 URL を検証する場合は、M3U8 暗号化と書き換えを有効にします。 デフォルトでは、MtsHlsUriToken が書き換えられます。 これにより、HLS データアクセス中にデータが保護されます。 復号化サービスの設定に使用されるコードは、M3U8 暗号化と書き換え機能が有効になっているかどうかによって異なります。 次のセクションでは、サンプルコードについて説明します。

    Java のサンプルコード

    次の表は、サンプルコードのパラメーターについて説明しています。 ビジネスニーズに基づいて変更できます。

    パラメーター

    説明

    region

    リージョン ID。 KMS と ApsaraVideo VOD は同じリージョンでアクティブ化する必要があります。 たとえば、中国(上海)で KMS と ApsaraVideo VOD をアクティブ化する場合は、このパラメーターに cn-shanghai を指定します。 その他のリージョンの ID については、「ApsaraVideo VOD のリージョン ID」をご参照ください。

    AccessKey

    Alibaba Cloud アカウントまたは RAM ユーザーの AccessKey ID と AccessKey シークレット。 AccessKey ペアの取得方法については、「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 = ""; // リージョン ID を設定します。
            // KMS へのアクセスを承認するために使用される AccessKey ペア。
            // Alibaba Cloud アカウントの AccessKey ペアには、すべての API 操作を呼び出す権限があります。 API 操作の呼び出しや日常的な O&M の実行には、RAM ユーザーの AccessKey ペアを使用することをお勧めします。
            // AccessKey ペア(AccessKey ID と AccessKey シークレット)をプロジェクトコードにハードコードしないことをお勧めします。 そうしないと、AccessKey ペアが漏洩し、アカウント内のすべてのリソースのセキュリティが侵害される可能性があります。
            // この例では、ApsaraVideo VOD は環境変数から AccessKey ペアを読み取って、API アクセスの ID 検証を実装します。 サンプルコードを実行する前に、次の環境変数を設定します。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. 復号化リクエストを受信し、EDK とトークンを取得します。
         * 2. KMS の DecryptKMSDataKey 操作を呼び出して DK を取得します。
         * 3. Base64 アルゴリズムを使用して DK をデコードし、DK を返します。
         */
        public class HlsDecryptHandler implements HttpHandler {
            /**
             * 復号化リクエストを処理します。
             * @param httpExchange
             * @throws IOException
             */
            public void handle(HttpExchange httpExchange) throws IOException {
                String requestMethod = httpExchange.getRequestMethod();
                if ("GET".equalsIgnoreCase(requestMethod)) {
                    // トークンが有効かどうかを確認します。
                    String token = getMtsHlsUriToken(httpExchange);
                    boolean validRe = validateToken(token);
                    if (!validRe) {
                        return;
                    }
                    // 動画 URL から EDK を取得します。
                    String ciphertext = getCiphertext(httpExchange);
                    if (null == ciphertext)
                        return;
                    // KMS から取得した DK を Base64 アルゴリズムを使用してデコードし、DK を返します。
                    byte[] key = decrypt(ciphertext);
                    // ヘッダーを設定します。
                    setHeader(httpExchange, key);
                    // Base64 アルゴリズムでデコードされた DK を返します。
                    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 の復号化操作を呼び出して DK を復号化し、Base64 アルゴリズムを使用して DK をデコードします。
             * @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 アルゴリズムを使用して DK をデコードする必要があります。
                    return Base64.decodeBase64(plaintext);
                } catch (ClientException e) {
                    e.printStackTrace();
                    return null;
                }
            }
            /**
             * 動画 URL から EDK を取得します。
             * @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("CipherText パラメーターが見つかりません");
                    return null;
                }
            }
    
              /**
             * M3U8 暗号化と書き換えに不可欠なトークンの有効性を確認します。
             * @param token
             * @return
             */
            private boolean validateToken(String token) {
                if (null == token || "".equals(token)) {
                    return false;
                }
                // TODO。 トークンが有効かどうかを確認するロジックをカスタマイズできます。
                return true;
            }
            /**
             * M3U8 暗号化と書き換えに不可欠な Token パラメーターを取得します。
             *
             * @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("MtsHlsUriToken パラメーターが見つかりません");
                    return null;
                }
            }
        }
        /**
         * サービスを開始します。
         *
         * @throws IOException
         */
        private void serviceBootStrap() throws IOException {
            HttpServerProvider provider = HttpServerProvider.provider();
            // リッスン用のカスタムポートを指定できます。 Web サーバーは、同じポートで最大 30 リクエストを同時に処理できます。
            HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8099), 30);
            httpserver.createContext("/", new HlsDecryptHandler());
            httpserver.start();
            System.out.println("hls 復号化サーバーが起動しました");
        }
        public static void main(String[] args) throws IOException {
            HlsDecryptServer server = new HlsDecryptServer();
            server.serviceBootStrap();
        }}
  2. 暗号化された動画の再生 URL と認証情報を取得します。

    GetVideoPlayAuth 操作を呼び出して再生認証情報を取得し、GetPlayInfo 操作を呼び出して再生 URL を取得します。

  3. 暗号化された動画を再生します。

    HLS 暗号化は、すべての HLS 互換プレーヤーをサポートしています。 自己開発プレーヤーまたは ApsaraVideo Player を使用して、暗号化された動画を再生できます。

    ApsaraVideo Player を使用して暗号化された動画を再生する場合は、トークンと認証情報を取得する必要があります。 詳細については、「暗号化された動画を再生する方法」をご参照ください。 サードパーティ製のプレーヤーを使用する場合は、再生ロジックを自分で実装します。

    次の手順では、ApsaraVideo Player が再生をテストする方法について説明します。

    (推奨)M3U8 暗号化と書き換えが有効になっている場合

    プロセス

    • プレーヤーは、M3U8 プレイリストファイルの EXT-X-KEY タグを解析して、EncryptConfigDecryptKeyUri 値に対応する復号化キー URI を取得します。

    • 不正アクセスを制限するために、プレーヤーは復号化操作をリクエストするときに認証情報(MtsHlsUriToken パラメーターを介して渡される)を含める必要があります。

    • プレーヤーは自動的に DecryptKMSDataKey 操作を呼び出して DK を取得し、暗号化された TS ファイルを復号化してから、通常の再生に進みます。

    • 動画再生 URL は https://demo.aliyundoc.com/encrypt-stream****-hd.m3u8 です。 リクエストには、MtsHlsUriToken パラメーターが含まれている必要があります。

    • 最終的なリクエスト URL は https://demo.aliyundoc.com/encrypt-stream****-hd.m3u8?MtsHlsUriToken=<トークン> です。

    • 復号化 URL は https://demo.aliyundoc.com?CipherText=ZjJmZGViNzUtZWY1Mi00Y2RlLTk3MTMtOT**** です。

    • 復号化の最終的なリクエスト URL は https://demo.aliyundoc.com?CipherText=ZjJmZGViNzUtZWY1Mi00Y2RlLTk3MTMtOT****&MtsHlsUriToken=<発行されたトークン> です。

M3U8 暗号化と書き換えが無効になっている場合

  1. 復号化サービスを設定します。

    動画を復号化するためのローカル HTTP サービスを設定します。

    DecryptKMSDataKey 操作を呼び出して DK を復号化します。 レスポンスの PlainText 値には DK が含まれています。 この DK は、GenerateKMSDataKey 操作へのレスポンスで返された PlainText パラメーターの Base64 デコードバージョンです。

    サンプルコード:

    Java のサンプルコード

    次の表は、サンプルコードのパラメーターについて説明しています。 ビジネスニーズに基づいて変更できます。

    パラメーター

    説明

    region

    リージョン ID。KMS と ApsaraVideo VOD は同じリージョンでアクティブ化する必要があります。たとえば、中国 (上海) で KMS と ApsaraVideo VOD をアクティブ化する場合は、このパラメーターに cn-shanghai を指定します。その他のリージョンの ID については、「ApsaraVideo VOD のリージョン ID」をご参照ください。

    AccessKey

    Alibaba Cloud アカウントまたは RAM ユーザーの AccessKey ID と AccessKey シークレット。AccessKey ペアの取得方法については、「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 ペア。
            // Alibaba Cloud アカウントの AccessKey ペアには、すべての API 操作を呼び出す権限があります。API 操作の呼び出しや日常的な O&M の実行には、RAM ユーザーの AccessKey ペアを使用することをお勧めします。
            // プロジェクトコードに AccessKey ペア (AccessKey ID と AccessKey シークレット) をハードコードしないでください。ハードコードすると、AccessKey ペアが漏洩し、アカウント内のすべてのリソースのセキュリティが侵害される可能性があります。
            // この例では、ApsaraVideo VOD は環境変数から AccessKey ペアを読み取って、API アクセスの ID 検証を実装します。サンプルコードを実行する前に、環境変数 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. 復号リクエストを受信し、EDK とトークンを取得します。
         * 2. KMS の DecryptKMSDataKey 操作を呼び出して、DK を取得します。
         * 3. Base64 アルゴリズムを使用して DK をデコードします。
         */
        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 から EDK を取得します。
                    String ciphertext = getCiphertext(httpExchange);
                    System.out.println(ciphertext);
                    if (null == ciphertext)
                        return;
                    // KMS から取得した DK を Base64 アルゴリズムを使用してデコードし、DK を返します。
                    byte[] key = decrypt(ciphertext);
                    // ヘッダーを設定します。
                    setHeader(httpExchange, key);
                    // Base64 アルゴリズムでデコードされた DK を返します。
                    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 の復号操作を呼び出して DK を復号し、Base64 アルゴリズムを使用して DK をデコードします。
             * @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);
                    // 注: DK は Base64 アルゴリズムを使用してデコードする必要があります。
                    return Base64.decodeBase64(plaintext);
                } catch (ClientException e) {
                    e.printStackTrace();
                    return null;
                }
            }
    
            /**
             * ビデオ URL から EDK を取得します。
             * @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("CipherText パラメータが見つかりません");
                    return null;
                }
            }
        }
    
        /**
         * サービスを開始します。
         *
         * @throws IOException
         */
        private void serviceBootStrap() throws IOException {
            HttpServerProvider provider = HttpServerProvider.provider();
            // リッスン用のカスタムポートを指定できます。Web サーバーは、同じポートで最大 30 リクエストを同時に処理できます。
            HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8099), 30);
            httpserver.createContext("/", new HlsDecryptHandler());
            httpserver.start();
            System.out.println("hls decrypt server started"); // hls 復号サーバーが起動しました
        }
        public static void main(String[] args) throws IOException {
            HlsDecryptServerNoToken server = new HlsDecryptServerNoToken();
            server.serviceBootStrap();
        }}
    
  2. 暗号化された動画の再生 URL と認証情報を取得します。

    GetVideoPlayAuth を取得する再生情報を取得する 操作を呼び出して再生認証情報を取得し、 操作を呼び出して再生 URL を取得します。

  3. 暗号化された動画を再生します。

    HLS 暗号化は、すべての HLS 互換プレーヤーをサポートしています。 自己開発プレーヤーまたは ApsaraVideo Player を使用して、暗号化された動画を再生できます。

    ApsaraVideo プレーヤーを使用して暗号化された動画を再生する場合は、トークンと認証情報を取得する必要があります。詳細については、「暗号化された動画を再生する方法」をご参照ください。サードパーティ製のプレーヤーを使用する場合は、再生ロジックを独自に実装してください。

    次の手順では、ApsaraVideo Playerの再生テスト方法について説明します。

    M3U8 暗号化と書き換えが無効になっている場合

    プロセス

    • プレーヤーは、M3U8 ファイルの EXT-X-KEY タグを解析して復号鍵 URI を取得します。これは、EncryptConfigDecryptKeyUri 値に対応します。

    • プレーヤーは自動的に DecryptKMSDataKey 操作を呼び出して DK を取得し、暗号化された TS ファイルを復号化してから、通常の再生に進みます。

    • ビデオ再生 URL は https://demo.aliyundoc.com/encrypt-stream****-hd.m3u8 です。

    • 最終的なリクエスト URL は https://demo.aliyundoc.com/encrypt-stream****-hd.m3u8 です。

    • 復号 URL は https://demo.aliyundoc.com?CipherText=ZjJmZGViNzUtZWY1Mi00Y2RlLTk3MTMtOT**** です。

    • 復号のための最終的なリクエスト URL は https://demo.aliyundoc.com?CipherText=ZjJmZGViNzUtZWY1Mi00Y2RlLTk3MTMtOT**** です。

関連情報