M3U8暗号化と書き換えを有効にすると、Alibaba Cloud CDNはHTTPライブストリーミング (HLS) 経由で送信されるM3U8ファイルを書き換えることができます。 M3U8ファイルが書き換えられた後、暗号化パラメータはファイルの #EXT-X-KEY
タグに追加されます。 暗号化パラメータは、暗号化アルゴリズム、鍵URI、および認証パラメータを含みます。 クライアントがAlibaba Cloud CDNによって書き換えられたM3U8ファイルを受信すると、クライアントは認証パラメーターを保持するキーURIを使用してリクエストを開始します。 リクエストはCDNエッジノードからキーを取得します。 次に、クライアントは暗号化アルゴリズムとキーを使用してトランスポートストリーム (TS) ファイルを復号化します。 M3U8暗号化および書き換えは、HLSデータ伝送を暗号化できます。
背景情報
HLSは、Apple Inc. によって開発されたHTTPベースの適応型ビットレートストリーミング通信プロトコルです。 HLSはHTTPに基づいています。 クライアントは、HTTPを介してサーバーからファイルを順番にダウンロードします。 HLSは、ビデオファイルがTSフォーマットでカプセル化されることを指定します。 TSビデオファイルとは別に、HLSは再生を制御するM3U8ファイルも指定します。 HLSは、伝送のためにビデオストリームをいくつかのTSビデオファイルに分割します。 ストリーミングメディアセッションの開始時に、クライアントは、まず、メディアプレイリストとして機能するTSファイルURLを含むM3U8ファイルをダウンロードします。 次に、クライアントはURLを使用してTSファイルをダウンロードします。
HLS基本フィールド:
#EXTM3U
: M3U8ファイルヘッダー。最初の行に配置する必要があります。EXT-X-MEDIA-SEQUENC
: 最初のTLファイルのシリアル番号。 ほとんどの場合、このシリアル番号は0です。 ライブストリーミングシナリオでは、このシリアル番号はストリーミングセグメントの開始位置を示します。 例:#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION
: 各TSファイルの最大長。 たとえば、#EXT-X-TARGETDURATION:10
は、各TSファイルの長さを10秒にすることを指定します。#EXT-X-ALLOW-CACHE
: ファイルをキャッシュできるかどうかを指定します。 有効な値:#EXT-X-ALLOW-CACHE:YES
および#EXT-X-ALLOW-CACHE:NO
。 ほとんどの場合、値はYESに設定されます。#EXT-X-ENDLIST
: M3U8ファイルのターミネータ。#EXTINF
: 長さや帯域幅など、TSファイルに関する情報が含まれます。 ほとんどの場合、パラメーターは#EXTINF:<duration>,[<title>]
形式で設定されます。 他の情報を値に追加できます。 コンマ (,) の前の値は、現在のTSファイルの長さを指定します。 TSファイルの長さは、#EXT-X-TARGETDURATIO
の値より小さくする必要があります。#EXT-X-バージョン
: M3U8のバージョン番号。#EXT-X-DISCONTINUITY
: 2つの連続したTSファイルが中断されることを指定します。#EXT-X-PLAYLIST-TYP
: ストリーミングメディアのタイプ。#EXT-X-KEY
: データを暗号化および解析するかどうかを指定します。 たとえば、#EXT-X-KEY:METHOD=AES-128、URI=" https://example.com/video.key?token=xxx "
は、暗号化アルゴリズムがAES-128であることを指定します。 クライアントは、鍵を取得する要求をhttps://example.com/video.key?token=xxx
に送信できます。 このキーは、TSファイルを解読するためにオンプレミスのマシンに保存されます。
制御ポリシー機能の動作
クライアントは、
http://example.com/media/index.m3u8?MtsHlsUriToken=xxx
に応じてM3U8ファイルschのリクエストをCDNエッジノードに送信します。エッジノードは要求を検証する。 リクエストは検証に合格します。
エッジノードは、オリジンサーバーからM3U8ファイルをダウンロードし、M3U8ファイルをキャッシュします。
エッジノードは、M3U8ファイルの
#EXT-X-KEY
タグを書き換え、暗号化アルゴリズム、キーURI、および認証パラメータ (#EXT-X-KEY:METHOD=AES-128、URI=" https://example.com/video.key?MtsHlsUriToken=xxx "
など) をタグに追加します。エッジノードは、書き換えられたM3U8ファイルをクライアントに送信します。
クライアントはM3U8ファイルを受信して解析し、キーURI
https://example.com/video.key?MtsHlsUriToken=xxx
を取得します。 次いで、クライアントは、URIに要求を送信します。エッジノードは、要求を受信して検証し、鍵ファイルをクライアントに送信します。
クライアントはM3U8ファイルの解析を続け、エッジノードからTSファイルをダウンロードします。
クライアントは、キーファイル内のキーと
#EXT-X-key
で指定された暗号化アルゴリズムを使用して、ダウンロードされたTSファイルを復号します。
シナリオ
HLSは、M3U8ファイルを使用してクライアントにメディアプレイリストを提供します。 クライアントがM3U8ファイルを受信すると、クライアントはビデオ再生を開始できます。 オリジンサーバー上のビデオファイルを不正アクセスから保護するために、Alibaba Cloud CDNはHLS経由で送信されるTSファイルを暗号化し、クライアントに復号化方法を通知する必要があります。 このタイプの暗号化を実装するために、Alibaba Cloud CDNはM3U8暗号化および書き換え機能をサポートしています。 この機能では、#EXT-X-KEY
タグを使用して、暗号化アルゴリズム、キーURI、および認証キーをクライアントに通知します。
手順
ApsaraVideo VODコンソールにログインし、[Parameter Pass-through for HLS Encryption] をオンにします。
詳細については、「HLS暗号化のパラメーターパススルー」をご参照ください。
CDNポイントオブプレゼンス (POP) にリクエストを送信して、M3U8ファイルにアクセスし、MtsHlsUriTokenをリクエストに含めます。
MtsHlsUriTokenを取得するには、MtsHlsUriTokenを発行するトークンサービスを設定する必要があります。
次のサンプルコードでは、MtsHlsUriTokenの生成方法の例を示します。 次の表に、ビジネス要件に基づいて変更できるサンプルJavaコードのパラメーターを示します。
項目
説明
ENCRYPT_KE
暗号化キー。 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 { // The following parameters are not required if you do not use the AES algorithm to generate a token. private static String ENCRYPT_KEY = ""; // A custom encryption key. The string must be 16, 24, or 32 characters in length. private static String INIT_VECTOR = ""; // A custom encryption offset. The string must be 16 characters in length and cannot contain special characters. 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)); // Verify the token. } /** * Generate a token based on the configured parameters. * Note: * 1. The parameters include the user ID and the type of the playback device. * 2. A token is generated when the token operation is called. * @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), "_"); // Set the validity period of a token to 30 seconds. You can change the value based on your business requirements. long expire = System.currentTimeMillis() + 30000L; base += "_" + expire; // A custom string that is 16 characters in length. In this example, 2 more characters are required because the timestamp contains 13 characters and the underscore (_) is 1 character in length. You can change the value of the base parameter. The value must be 16, 24, or 32 characters in length. // Generate a token. String token = encrypt(base, ENCRYPT_KEY); // arg1 is the custom string to be encrypted. arg2 is the encryption key. // Save the token. The validity of the token is verified during decryption, such as the validity period and the number of times the token is used. saveToken(token); return token; } /** * Check whether the token is valid. * Note: * 1. Before the decryption operation returns the playback key, the decryption service checks whether the token is legitimate and valid. * 2. We recommend that you check the validity period of the token and the number of times the token is used. * @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 is the string to be decrypted. arg2 is the decryption key. // Check the validity period of the token. Long expireTime = Long.valueOf(base.substring(base.lastIndexOf("_") + 1)); System.out.println("Check the validity period:" + expireTime); if (System.currentTimeMillis() > expireTime) { return false; } // Obtain the token information from the database and check whether the token is valid. You can modify the logic. TokenInfo dbToken = getToken(token); // Check whether the token has been used. if (dbToken == null || dbToken.useCount > 0) { return false; } // Obtain the business attributes for verification. String businessInfo = base.substring(0, base.lastIndexOf("_")); String[] items = businessInfo.split("_"); // Verify the validity of the business attributes. You can customize the logic. return validateInfo(items); } /** * Save the token to the database. * Create custom logic. * * @param token */ public void saveToken(String token) { // TODO. Save the token. } /** * Query the token. * Create custom logic. * * @param token */ public TokenInfo getToken(String token) { // TODO. Obtain the token from the database and check whether the token is valid. return null; } /** * Verify the validity of the business attributes. You can customize the logic. * * @param infos * @return */ public boolean validateInfo(String... infos) { // TODO. Verify the validity of the information, such as the UID. return true; } /** * Generate a token by using the AES algorithm. * * @param encryptStr The string to be encrypted. * @param encryptKey The encryption 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); } /** * Decrypt the token by using the AES algorithm. * * @param decryptStr The string to be decrypted. * @param decryptKey The decryption 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); } /** * Obtain the token information. The sample code is for reference only. You can obtain additional token information based on your business scenario. */ class TokenInfo { // Obtain the number of times that the token is used. Modifications must be synchronized in a distributed environment. int useCount; // The content of the token. String token; }}
POPがリクエストを受信し、リクエストが認証に合格した後にファイルを再生します。
手順2で生成された
MtsHlsUriToken
の値がtest
の場合、Alibaba Cloud CDNは、M3U8ファイルの#EXT-X-KEY
タグの下のURIの末尾にMtsHlsUriToken=test
を追加して、ファイルを復号します。認証ロジックを開発する必要があります。 詳細については、「HLS暗号化」トピックの「HLS暗号化ビデオの再生」セクションをご参照ください。