HTTPライブストリーミング (HLS) 暗号化は、Key Management Service (KMS) およびトークンサービスと一緒に使用する必要があります。 このトピックでは、HLS暗号化の用語、準備、および関連手順について説明します。
用語
KMS
KMSは、データキーの生成、暗号化、復号化に使用されるセキュリティ管理サービスです。
RAM
Resource Access Management (RAM) では、ユーザーIDを管理し、リソースへのアクセスを制御できます。
DK
データキー (DK) はプレーンテキストキーと呼ばれ、データの暗号化に使用されます。
EDK
エンベロープデータ鍵 (EDK) は暗号文鍵とも呼ばれ、エンベロープ暗号化を使用して生成されます。
準備
ApsaraVideo VODを有効にし、ApsaraVideo VODコンソールにログインして、リージョンでバケットを有効にします。 詳細については、「手順1: ストレージ管理の有効化」をご参照ください。
ApsaraVideo VODコンソールで、高速化ドメイン名を設定し、ドメイン名のビデオ関連の [Parameter Pass-through for HLS Encryption] をオンにします。 この機能を有効にすると、MtsHlsUriTokenパラメーターを書き換えることができます。 詳細については、「CDNのドメイン名の追加」および「HLS暗号化のパラメーターパススルー」をご参照ください。
RAMコンソールにログインします。 AccessKey IDとAccessKey secretを取得して保存します。
KMSを有効にしてサービスキーを取得します。
説明サービスキーは、KMSで使用されるプライマリ暗号化キーです。 HLS暗号化のキーを生成するには、サービスキーを使用する必要があります。
サービスキーは、ビデオが保存されているオリジンサーバーと同じリージョンに作成する必要があります。 たとえば、ビデオが中国 (上海) リージョンに保存されている場合は、中国 (上海) リージョンにサービスキーを作成する必要があります。
ApsaraVideo VODコンソールにログインします。 左側のナビゲーションウィンドウで、 を選択します。 ページの左上隅で、サービスキーを作成するリージョンを選択し、[サービスキーの作成] をクリックします。
サービスキーの作成後、GenerateDataKey操作を呼び出し、
KeyId
をalias/acs/vodに設定します。 返されたKeyId
は、以降のトランスコード処理で使用されます。
ビジネス要件に基づいてサーバーSDKを設定します。 詳細については、「インストール」をご参照ください。
暗号化手順
コード変換テンプレートと非コード変換テンプレートを追加します。
HLSの暗号化とトランスコードには、2つのテンプレートが必要です。
リージョンでバケットを有効にすると、トランスコーディングなしテンプレートが自動的に生成されます。
説明デフォルトでは、ApsaraVideo VODはアップロードされたビデオを自動的にトランスコードします。 自動トランスコーディングはHLS暗号化をサポートしていません。 HLS暗号化を使用するときに自動トランスコードを防ぐには、トランスコードなしテンプレートを使用してビデオをアップロードする必要があります。 次に、SubmitTranscodeJobs操作を呼び出して、HLSの暗号化とトランスコーディングを開始できます。
暗号化が設定されたコード変換テンプレートを作成し、テンプレートIDを記録するには、次の手順を実行します。
ApsaraVideo VODコンソールにログインします。
左側のナビゲーションウィンドウで、 を選択します。 表示されるページで、[トランスコードテンプレートグループの作成] をクリックします。
コード変換テンプレートグループの作成ページで、テンプレートグループ名フィールドに名前を入力します。
通常のトランスコードテンプレートセクションで、テンプレートの追加をクリックしてコード変換テンプレートを作成します。
基本パラメータセクションで、カプセル化フォーマットドロップダウンリストからhlsを選択します。
[ビデオパラメーター] 、[オーディオパラメーター] 、および [条件付きトランスコードパラメーター] セクションで、ビジネス要件に基づいてパラメーターを設定します。 各パラメーターの意味と制限の詳細については、「オーディオとビデオのトランスコード」をご参照ください。
[高度なパラメーター] セクションで、[ビデオ暗号化] をオンにし、暗号化方法にAlibaba Cloud独自の暗号化を選択したままにします。
説明SubmitTranscodeJobs操作を呼び出すときに、このテンプレートを使用するTemplateGroupIdパラメーターを指定できます。 次に、ApsaraVideo VODは、テンプレートの設定とキーに基づいてビデオを暗号化およびトランスコードします。
[保存] をクリックします。 [トランスコードテンプレートグループ] ページで、暗号化テンプレートIDを取得して記録します。
ApsaraVideo VODにKMSリソースへのアクセスを許可します。
RAMを使用してApsaraVideo VOD権限を付与してKMSリソースにアクセスするには、[クラウドリソースアクセス権限付与] ページで [権限付与ポリシーの確認] をクリックします。
Alibaba Cloud KMSをカプセル化するキー管理サービスを設定します。
GenerateDataKey操作を呼び出してAES_128キーを生成する場合、KeyIdパラメーターをサービスキーのIDに設定し、KeySpecパラメーターをAES_128に設定するだけで済みます。 上記のパラメーターを設定しないと、暗号化が失敗する可能性があります。
呼び出しが成功したら、返されたパラメーター
CiphertextBlob
の値を記録します。 パラメーターは暗号文キーを示します。説明キーの使用に対して課金されます。 詳細については、「KMSの課金」をご参照ください。
MtsHlsUriTokenを生成するためのトークン発行サービスを設定します。
次の内容は、手動変更が必要なJavaサンプルコードとコード内のパラメーターを示しています。
ENCRYPT_KEY: 暗号化用のカスタム文字列。 文字列の長さは16文字です。
INIT_VECTOR: カスタム文字列。 文字列の長さは16文字で、特殊文字は使用できません。
playToken.generateToken(""): カスタム文字列。 文字列の長さは16文字です。
最終コードによって生成されるトークンはMtsHlsUriTokenです。
import com.sun.deploy.util.StringUtils; import org.apache.commons.codec.binary.Base64; 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. // A custom string for encryption. private static String ENCRYPT_KEY = ""; // A custom string that is 16 characters in length and cannot contain special characters. private static String INIT_VECTOR = ""; public static void main(String[] args) throws Exception { PlayToken playToken = new PlayToken(); playToken.generateToken(""); } /** * 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; // 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. Make sure that the value is 16 characters in length. base += "_" + expire; // Generate a token. String token = encrypt(base, ENCRYPT_KEY); System.out.println(token); // 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); // Check the validity period of the token. Long expireTime = Long.valueOf(base.substring(base.lastIndexOf("_") + 1)); 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. Token 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("_"); // Check the validity of the business attributes. You can create custom 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 Token getToken(String token) { // TODO. Obtain the token from the database and check whether the token is valid. return null; } /** * Check the validity of the business attributes. You can create a custom logic. * * @param infos * @return */ public boolean validateInfo(String... infos) { // TODO. Check the validity of the information, such as the UID. return true; } /** * Generate a token by using the AES algorithm. * * @param key * @param value * @return * @throws Exception */ public String encrypt(String value, String key) throws Exception { IvParameterSpec e = new IvParameterSpec(INIT_VECTOR.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, e); byte[] encrypted = cipher.doFinal(value.getBytes()); return Base64.encodeBase64String(encrypted); } /** * Decrypt the token by using the AES algorithm. * * @param key * @param encrypted * @return * @throws Exception */ public String decrypt(String encrypted, String key) throws Exception { IvParameterSpec e = new IvParameterSpec(INIT_VECTOR.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, e); byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted)); return new String(original); } /** * Obtain the token information. This sample code is for reference only. You can obtain more token information based on your business scenario. */ class Token { // Obtain the number of times that the token can be used. Modifications must be synchronized in a distributed environment. int useCount; // The content of the token. String token; }}
復号化サービスを設定します。
重要ビデオを再生する前に、復号化サービスを開始する必要があります。 それ以外の場合、ビデオは期待どおりに復号できません。
KMSのDecrypt操作を呼び出してEDKを復号します。 復号化操作を認証する場合は、トークン発行サービスを設定し、発行されたトークンが復号化サービスによって解析および検証できることを確認します。
GenerateDataKey操作は、暗号文キーと平文キーを生成するために使用されます。 平文キーはBase64-encodedです。 解読操作は、Base64アルゴリズムを使用してデコードされたキーを返します。
次の内容は、手動変更が必要なJavaサンプルコードとコード内のパラメーターを示しています。
region: リージョンIDを入力します。 たとえば、中国 (上海) のcn-shanghaiを指定します。
AccessKey: アカウントのAccessKey IDとAccessKeyシークレットを入力します。
httpserver: ビジネス要件に基づいてサービスを開始するポート番号を入力します。
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.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 { // The region where KMS is used. The region must be the same as the region where the video resides. String region = ""; // The AccessKey pair that is authorized to access KMS. // The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. We recommend that you use a RAM user to call API operations or perform routine O&M. // We recommend that you do not include your AccessKey pair (AccessKey ID and AccessKey secret) in your project code. Otherwise, the AccessKey pair may be leaked and the security of all resources within your account may be compromised. // In this example, ApsaraVideo VOD reads the AccessKey pair from the environment variables to implement identity verification for API access. Before you run the sample code, configure the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and 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)); } /** * Note: * 1. Receive a decryption request and obtain the EDK and token. * 2. Call the decryption operation of KMS to obtain the DK. * 3. Decode the DK by using the Base64 algorithm and return the DK. */ public class HlsDecryptHandler implements HttpHandler { /** * Process the decryption request. * @param httpExchange * @throws IOException */ public void handle(HttpExchange httpExchange) throws IOException { String requestMethod = httpExchange.getRequestMethod(); if ("GET".equalsIgnoreCase(requestMethod)) { // Check whether the token is valid. String token = getMtsHlsUriToken(httpExchange); boolean validRe = validateToken(token); if (!validRe) { return; } // Obtain the EDK from the video URL. String ciphertext = getCiphertext(httpExchange); if (null == ciphertext) return; // Decrypt the key in KMS and decode the key by using the Base64 algorithm. byte[] key = decrypt(ciphertext); // Configure the headers. setHeader(httpExchange, key); // Return the key that is decoded by using the Base64 algorithm. 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); } /** * Call the decryption operation of KMS to decrypt the key and decode the key by using the Base64 algorithm. * @param ciphertext * @return */ 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(); // Note: You must decode the key by using the Base64 algorithm. return Base64.decodeBase64(plaintext); } catch (ClientException e) { e.printStackTrace(); return null; } } /** * Check whether the token is valid. * @param token * @return */ private boolean validateToken(String token) { if (null == token || "".equals(token)) { return false; } // TODO. You can create custom logic to check whether the token is valid. return true; } /** * Obtain the EDK from the video 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; } } /** * Obtain the value of the token parameter. * * @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; } } } /** * Start the service. * * @throws IOException */ private void serviceBootStrap() throws IOException { HttpServerProvider provider = HttpServerProvider.provider(); // You can specify a custom listening port. The listening port can receive up to 30 requests at the same time. 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(); }}
ビデオをアップロードします。
[トランスコーディングなし] テンプレートを使用して、ビデオのアップロードに使用できる資格情報とURLを作成します。 ApsaraVideo VODコンソールを使用してビデオをアップロードする方法の詳細については、「ApsaraVideo VODコンソールを使用したメディアファイルのアップロード」をご参照ください。 サーバー操作を呼び出してビデオをアップロードする方法の詳細については、「CreateUploadVideo」をご参照ください。
アップロードコールバックメッセージを受信します。
SetMessageCallback操作を呼び出してコールバックを構成し、GetMessageCallback操作を呼び出してコールバックメッセージを照会します。 FileUploadCompleteのコールバックメッセージを受信すると、ビデオはApsaraVideo VODにアップロードされます。
HLS暗号化とトランスコードの開始
SubmitTranscodeJobs操作を呼び出して、HLSの暗号化とトランスコードを開始します。
次の内容は、手動変更が必要なJavaサンプルコードとコード内のパラメーターを示しています。
request.setTemplateGroupId(""): 暗号化テンプレートのIDを指定します。
request.setVideoId(""): ビデオIDを指定します。
encryptConfig.put("CipherText","): 手順3で取得したCiphertextBlobパラメーターの値を指定します。
encryptConfig.put("DecryptKeyUri","): 再生URL、
CiphertextBlob
の値、およびMtsHlsUriToken
の値を指定します。 たとえば、ローカルポート8099が使用されている場合、再生URLはhttp:// 172.16.0.1:8099?CipherText=CiphertextBlob value&MtsHlsUriToken=MtsHlsUriToken value
です。
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 { // The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. We recommend that you use a RAM user to call API operations or perform routine O&M. // We recommend that you do not include your AccessKey pair (AccessKey ID and AccessKey secret) in your project code. Otherwise, the AccessKey pair may be leaked and the security of all resources within your account may be compromised. // In this example, ApsaraVideo VOD reads the AccessKey pair from the environment variables to implement identity verification for API access. Before you run the sample code, configure the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and 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 { // Specify the region where ApsaraVideo VOD is activated. 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(); } } }
HLS暗号化とトランスコードが成功したかどうかを確認
ApsaraVideo VODコンソールにログインして、ビデオURLを表示できます。 HLS暗号化が成功したかどうかを確認するには、次の方法を使用します。
ビデオを暗号化およびトランスコードした後、M3U8形式のURLが1つだけ生成された場合、ビデオのトランスコードに失敗しました。
ビデオを暗号化およびトランスコードした後、ビデオのM3U8 URLが生成され、MP4などの元のフォーマットのファイルが存在する場合、フォーマット列にAlibaba Cloud Proprietary Cryptographyフラグが表示されているかどうかを確認することで、HLS暗号化が成功したかどうかを判断できます。 ほとんどの場合、フォーマット列に「Alibaba Cloud独自暗号」と表示されている場合、HLS暗号化は成功します。
上記の方法が失敗した場合は、暗号化フラグを含むM3U8ファイルのURLをコピーし、
curl -v "M3U8ファイルのURL"
コマンドを実行して、取得したコンテンツに鍵情報が含まれているかどうかを確認します。URI="<HLS暗号化の開始時に渡す復号化URL。EncryptConfigのDecryptKeyUriパラメーターの値>"
鍵情報が存在する場合、HLS暗号化は成功です。
再生手順
ビデオの再生URLと資格情報を取得します。
GetPlayInfoおよびGetVideoPlayAuth操作を呼び出して、ビデオの再生URLと資格情報を取得します。
認証情報を指定します。
プレーヤーは、M3U8ファイルのURIを取得した後、M3U8ファイルのEXT-X-KEYタグ内のURIを解析してアクセスします。 このようにして、プレーヤは、暗号文鍵を含む復号化操作のURIを取得します。 このURIは、HLS暗号化の開始時に指定するEncryptConfigの
DecryptKeyUri
パラメーターの値です。許可されたユーザーのみがビデオにアクセスする場合、プレーヤーは、プレーヤーが復号化キーを取得したときに確認した認証情報を提供する必要があります。 認証情報は、MtsHlsUriTokenパラメーターを使用して指定できます。
例:
ビデオ再生URLは
https://demo.aliyundoc.com/encrypt-stream ****-hd.m3u8
です。 リクエストにはMtsHlsUriToken
パラメーターを含める必要があります。最後のリクエストURLは
https://demo.aliyundoc.com/encrypt-stream ****-hd.m3u8?MtsHlsUriToken=<Token>
です。復号化URLは
https://demo.aliyundoc.com?Ciphertext=ZjJmZGViNzUtZWY1Mi00Y2RlLTk3MTMtOT ****
です。復号化の最終的なリクエストURLは、
https://demo.aliyundoc.com?Ciphertext=ZjJmZGViNzUtZWY1Mi00Y2RlLTk3MTMtOT ****&MtsHlsUriToken=<発行されたトークン>
です。
ビデオを再生します。
プレーヤーが解読URIを取得した後、プレーヤーは自動的に解読キーを取得する要求を送信します。 プレーヤが復号鍵を取得した後、プレーヤは、再生のために暗号化されたTSファイルを復号します。