全部產品
Search
文件中心

ApsaraVideo Media Processing:HLS資料加密

更新時間:Nov 12, 2025

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

使用情境

HLS標準資料加密適用於對視頻進行保護的情境,可以防止非法下載和非法傳播。

使用說明

  • HLS標準資料加密需要使用SubmitJobs介面。

  • 工作流程中不可使用HLS標準資料加密Base64方式。

  • 標準加密技術原理請參考HLS標準加密

範例程式碼依賴

  • MPS SDK詳情參見 安裝

  • 其他依賴:

    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>kms20160120</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.68.noneautotype</version>
    </dependency>
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.9</version>
    </dependency>

KMS方式加密(推薦)

加密轉碼

說明

如果您選擇使用工作流程來觸發標準加密轉碼,您可以將檔案重新上傳至工作流程指定的輸入路徑,或者通過使用AddMedia - 新增媒體來觸發工作流程。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;
import com.aliyun.tea.*;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class SubmitJobsForKMS {
    /**
     * <b>description</b> :
     * <p>使用AK&amp;SK初始化MPS帳號Client</p>
     * @return Client
     *
     * @throws Exception
     */
    public static com.aliyun.mts20140618.Client createMTSClient() throws Exception {

        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                // 必填,請確保代碼運行環境設定了環境變數 ALIBABA_CLOUD_ACCESS_KEY_ID。
                .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                // 必填,請確保代碼運行環境設定了環境變數 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        config.endpoint = "mts.cn-shanghai.aliyuncs.com";
        return new com.aliyun.mts20140618.Client(config);
    }

    /**
     * <b>description</b> :
     * <p>使用AK&amp;SK初始化KMS帳號Client</p>
     * @return Client
     *
     * @throws Exception
     */
    public static com.aliyun.kms20160120.Client createKMSClient() throws Exception {

        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                // 必填,請確保代碼運行環境設定了環境變數 ALIBABA_CLOUD_ACCESS_KEY_ID。
                .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                // 必填,請確保代碼運行環境設定了環境變數 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        config.endpoint = "kms.cn-shanghai.aliyuncs.com";
        return new com.aliyun.kms20160120.Client(config);
    }


    /**
     * 提交KMS加密轉碼
     * @return
     * @throws Exception
     */
    public static void submitJobsForKMS() throws Exception {

        //擷取ciphertextBlob
        JSONObject generateDataKey = getEncryptionConfigs();
        String ciphertextBlob = generateDataKey.getString("ciphertextBlob");
        System.out.println(ciphertextBlob);
        //構建加密參數
        JSONObject encryption = new JSONObject();
        encryption.put("Type", "hls-aes-128");
        encryption.put("Key", ciphertextBlob);
        String url = "http://127.0.0.1:8888?Ciphertext=" + ciphertextBlob; // 解密地址,按需配置
        encryption.put("KeyUri", Base64.encodeBase64URLSafeString(url.getBytes()));
        encryption.put("KeyType", "KMS");
        //構建input, 需要保證Location地區和服務client地區一致
        JSONObject input = new JSONObject();
        input.put("Bucket", "<your bucket name>");
        input.put("Location", "oss-cn-shanghai");

        //構建一個輸出對象
        JSONObject output = new JSONObject();
        try {
            input.put("Object", URLEncoder.encode("mps-test/input/test.mp4", "utf-8"));
            output.put("OutputObject", URLEncoder.encode("mps-test/output/test", "utf-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("input URL encode failed");
        }
        output.put("TemplateId", "<transcode templateId>");
        output.put("Encryption", encryption.toJSONString());
        JSONArray outputs = new JSONArray();
        outputs.add(output);
        //提交標準加密轉碼
        com.aliyun.mts20140618.Client client = SubmitJobsForKMS.createMTSClient();
        com.aliyun.mts20140618.models.SubmitJobsRequest submitJobsRequest = new com.aliyun.mts20140618.models.SubmitJobsRequest()
                //作業輸入
                .setInput(input.toJSONString())
                //作業輸出配置
                .setOutputs(outputs.toJSONString())
                //輸出檔案所在的OSS Bucket
                .setOutputBucket("<your bucket name>")
                //輸出檔案所在的 OSS Bucket 的地區(OSS Region)
                .setOutputLocation("oss-cn-shanghai")
                //管道ID
                .setPipelineId("<transcode pipelineId>");
        com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
        try {
            // 複製代碼運行請自行列印 API 的傳回值
            client.submitJobsWithOptions(submitJobsRequest, runtime);
        } catch (TeaException error) {
            // 此處僅做列印展示,請謹慎對待異常處理,在工程專案中切勿直接忽略異常。
            // 錯誤 message
            System.out.println(error.getMessage());
            // 診斷地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
        } catch (Exception _error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            // 此處僅做列印展示,請謹慎對待異常處理,在工程專案中切勿直接忽略異常。
            // 錯誤 message
            System.out.println(error.getMessage());
            // 診斷地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
        }


    }
    /**
     * 請求KMS擷取加密配置
     * @returng
     */
    private static JSONObject getEncryptionConfigs() throws Exception  {
        com.aliyun.kms20160120.Client client = SubmitJobsForKMS.createKMSClient();
        com.aliyun.kms20160120.models.GenerateDataKeyRequest generateDataKeyRequest = new com.aliyun.kms20160120.models.GenerateDataKeyRequest();
        //需換成KMS控制台的別名為alias/acs/mts的密鑰。
        generateDataKeyRequest.setKeyId("3f27f*************f");
        generateDataKeyRequest.setKeySpec("AES_128");

        com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
        try {
            // 複製代碼運行請自行列印 API 的傳回值
            com.aliyun.kms20160120.models.GenerateDataKeyResponse generateDataKeyResponse = client.generateDataKeyWithOptions(generateDataKeyRequest, runtime);
            return JSONObject.parseObject(JSON.toJSONString(generateDataKeyResponse.getBody()));
        } catch (TeaException error) {
            // 此處僅做列印展示,請謹慎對待異常處理,在工程專案中切勿直接忽略異常。
            // 錯誤 message
            System.out.println(error.getMessage());
            // 診斷地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
            return null;
        } catch (Exception _error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            // 此處僅做列印展示,請謹慎對待異常處理,在工程專案中切勿直接忽略異常。
            // 錯誤 message
            System.out.println(error.getMessage());
            // 診斷地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
            return null;
        }
    }

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

解密服務

import com.aliyun.tea.TeaException;
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;

public class DecryptServerKMS {

    public static void main(String[] args) throws IOException {
        DecryptServerKMS server = new DecryptServerKMS();
        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) {

            try {
                com.aliyun.kms20160120.Client client = DecryptServerKMS.createKMSClient();
                com.aliyun.kms20160120.models.DecryptRequest decryptRequest = new com.aliyun.kms20160120.models.DecryptRequest();
                //需換成KMS控制台的別名為alias/acs/mts的密鑰。
                decryptRequest.setCiphertextBlob(ciphertext);
                com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
                // 複製代碼運行請自行列印 API 的傳回值
                com.aliyun.kms20160120.models.DecryptResponse decryptResponse = client.decryptWithOptions(decryptRequest, runtime);
                String plaintext = decryptResponse.getBody().getPlaintext();
                return Base64.decodeBase64(plaintext);
            } catch (TeaException error) {
                // 此處僅做列印展示,請謹慎對待異常處理,在工程專案中切勿直接忽略異常。
                // 錯誤 message
                System.out.println(error.getMessage());
                // 診斷地址
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
                return null;
            } catch (Exception _error) {
                TeaException error = new TeaException(_error.getMessage(), _error);
                // 此處僅做列印展示,請謹慎對待異常處理,在工程專案中切勿直接忽略異常。
                // 錯誤 message
                System.out.println(error.getMessage());
                // 診斷地址
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
                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");
    }


    /**
     * <b>description</b> :
     * <p>使用AK&amp;SK初始化KMS帳號Client</p>
     * @return Client
     *
     * @throws Exception
     */
    public static com.aliyun.kms20160120.Client createKMSClient() throws Exception {

        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                // 必填,請確保代碼運行環境設定了環境變數 ALIBABA_CLOUD_ACCESS_KEY_ID。
                .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                // 必填,請確保代碼運行環境設定了環境變數 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        config.endpoint = "kms.cn-shanghai.aliyuncs.com";
        return new com.aliyun.kms20160120.Client(config);
    }


}

Base64方式加密

加密轉碼

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.tea.TeaException;
import org.apache.commons.codec.binary.Base64;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class SubmitJobsForBase64 {

    /**
     * <b>description</b> :
     * <p>使用AK&amp;SK初始化MPS帳號Client</p>
     * @return Client
     *
     * @throws Exception
     */
    public static com.aliyun.mts20140618.Client createClient() throws Exception {

        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                // 必填,請確保代碼運行環境設定了環境變數 ALIBABA_CLOUD_ACCESS_KEY_ID。
                .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                // 必填,請確保代碼運行環境設定了環境變數 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        config.endpoint = "mts.cn-shanghai.aliyuncs.com";
        return new com.aliyun.mts20140618.Client(config);
    }

    /**
     * 構建加密配置
     * @return
     */
    private static JSONObject getEncryptionConfigs() throws Exception  {
        JSONObject encryption = new JSONObject();
        encryption.put("Type", "hls-aes-128");
        //加密字串必須是16位
        encryption.put("Key", Base64.encodeBase64URLSafeString("encryptionkey128".getBytes()));
        //url為您的解密服務地址
        String url = "http://127.0.0.1:8888";
        encryption.put("KeyUri", Base64.encodeBase64URLSafeString(url.getBytes()));
        encryption.put("KeyType", "Base64");
        return encryption;
    }

    /**
     * 提交KMS加密轉碼
     * @return
     * @throws Exception
     */
    public static void submitJobsForBase64() throws Exception {

        //構建input, 需要保證Location地區和服務client地區一致
        JSONObject input = new JSONObject();
        input.put("Bucket", "<your bucket name>");
        input.put("Location", "oss-cn-shanghai");

        //構建一個輸出對象
        JSONObject output = new JSONObject();
        try {
            input.put("Object", URLEncoder.encode("mps-test/input/test.mp4", "utf-8"));
            output.put("OutputObject", URLEncoder.encode("mps-test/output/test", "utf-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("input URL encode failed");
        }
        output.put("TemplateId", "<transcode templateId>");
        output.put("Encryption", getEncryptionConfigs());
        JSONArray outputs = new JSONArray();
        outputs.add(output);
        //提交標準加密轉碼
        com.aliyun.mts20140618.Client client = SubmitJobsForKMS.createMTSClient();
        com.aliyun.mts20140618.models.SubmitJobsRequest submitJobsRequest = new com.aliyun.mts20140618.models.SubmitJobsRequest()
                //作業輸入
                .setInput(input.toJSONString())
                //作業輸出配置
                .setOutputs(outputs.toJSONString())
                //輸出檔案所在的OSS Bucket
                .setOutputBucket("<your bucket name>")
                //輸出檔案所在的 OSS Bucket 的地區(OSS Region)
                .setOutputLocation("oss-cn-shanghai")
                //管道ID
                .setPipelineId("<transcode pipelineId>");
        com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
        try {
            // 複製代碼運行請自行列印 API 的傳回值
            client.submitJobsWithOptions(submitJobsRequest, runtime);
        } catch (TeaException error) {
            // 此處僅做列印展示,請謹慎對待異常處理,在工程專案中切勿直接忽略異常。
            // 錯誤 message
            System.out.println(error.getMessage());
            // 診斷地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
        } catch (Exception _error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            // 此處僅做列印展示,請謹慎對待異常處理,在工程專案中切勿直接忽略異常。
            // 錯誤 message
            System.out.println(error.getMessage());
            // 診斷地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
        }

    }

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

解密服務

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;

public class DecryptServerBase64 {

    public static void main(String[] args) throws IOException {
        DecryptServerBase64 server = new DecryptServerBase64();
        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();
        //監聽連接埠8888,能同時接受30個請求
        HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8888), 30);
        httpserver.createContext("/", new Base64DecryptHandler());
        httpserver.start();
        System.out.println("base64 hls decrypt server started");
    }
}

相關文檔

加密