All Products
Search
Document Center

ApsaraVideo Media Processing:Perform HLS encryption

Last Updated:Aug 25, 2023

Scenarios

HTTP-Live-Streaming (HLS) encryption is used for basic data protection scenarios such as protecting your videos from illegal download or distribution.

Usage notes

  • You must call the SubmitJobs operation to use HLS encryption.

  • The Base64 algorithm cannot be used for HLS encryption in a workflow.

Dependencies

  • Install ApsaraVideo Media Processing (MPS) SDK for Java. For more information, see Install Alibaba Cloud SDK for Java.

  • Add other dependencies.

    <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>

Sample code for HLS encryption by using the Base64 algorithm

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.mps.sdk.utils.InitClient;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.mts.model.v20140618.SubmitJobsRequest;
import com.aliyuncs.mts.model.v20140618.SubmitJobsResponse;
import org.apache.commons.codec.binary.Base64;

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

/**
 * *****   Usage notes     ******
 * This sample code performs HLS encryption by using the Base64 algorithm. You must decrypt an encrypted video before you play it. For more information, see the "Sample code for decryption by using the Base64 algorithm" section of this topic.
 * The KeyUri parameter specifies the endpoint used to obtain the decryption key. Make sure that the specified key uniform resource identifier (URI) is valid. Otherwise, the decryption fails.
 */
public class Base64EncryptionTranscode {

    // The ID of the MPS queue. To view the ID of an MPS queue, log on to the MPS console and choose Global Settings > MPS Queue and Callback in the left-side navigation pane.
    private static String pipelineId = "b1a2b2cfe40a0cbf****";
    // The ID of the template. For more information about preset templates, see "Preset template details" at https://www.alibabacloud.com/help/zh/apsaravideo-for-media-processing/latest/yuzhimobanxiangqing.
    private static String templateId = "S00000001-100020";

    private DefaultAcsClient client = null;

    public Base64EncryptionTranscode() throws ClientException {

        this.client = InitClient.initMpsClient();
    }

    public static void main(String[] args) throws ClientException {
        Base64EncryptionTranscode base64EncryptionTranscode = new Base64EncryptionTranscode();
        base64EncryptionTranscode.submitJobs();
    }

    /**
     * Submit a transcoding job.
     * @throws ClientException
     */
    private void submitJobs() throws ClientException {

        JSONObject inputFile = new JSONObject();
        inputFile.put("Location", "oss-cn-beijing");
        inputFile.put("Bucket", "<your bucket name>");
        try {
            inputFile.put("Object", URLEncoder.encode("mps-test/demo/test.mp4", "utf-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("input URL encode failed");
        }

        SubmitJobsRequest request = new SubmitJobsRequest();
        request.setInput(inputFile.toJSONString());
        request.setInput(inputFile.toJSONString());
        request.setOutputLocation("oss-cn-beijing");
        request.setOutputBucket("<your bucket name>"");

        JSONArray outputs = new JSONArray();

        JSONObject output = new JSONObject();
        // The path of the output file. You do not need to specify the .m3u8 suffix for the output file. In this example, the hls-test.m3u8 file is generated.
        // Make sure that the output file does not trigger a workflow.
        output.put("OutputObject", "mps-test/demo-v2/hls-test");
        output.put("TemplateId", templateId);
        output.put("Encryption", getEncryptionConfigs());

        outputs.add(output);

        request.setOutputs(outputs.toJSONString());
        request.setPipelineId(pipelineId);

        SubmitJobsResponse response = client.getAcsResponse(request);
        System.out.println("RequestId is:" + response.getRequestId());
        System.out.println("JobId is:" + response.getJobResultList().get(0).getJob().getJobId());
        System.out.println("Response is:" + JSON.toJSONString(response));
    }

    /**
     * Build encryption configurations.
     * @return
     * @throws ClientException
     */
    private JSONObject getEncryptionConfigs() throws ClientException  {
        JSONObject encryption = new JSONObject();
        encryption.put("Type", "hls-aes-128");
        // The encryption key must be 16 bytes in length.
        encryption.put("Key", Base64.encodeBase64URLSafeString("encryptionkey128".getBytes()));
        // The endpoint used to obtain the decryption key.
        String url = "http://127.0.0.1:8888";
        encryption.put("KeyUri", Base64.encodeBase64URLSafeString(url.getBytes()));
        encryption.put("KeyType", "Base64");
        return encryption;
    }



}

Sample code for HLS encryption by using KMS

To perform HLS encryption by using Key Management Service (KMS), you must create a workflow for HLS encryption. You can call the SubmitJobs operation to perform HLS encryption only after the workflow is run once. For more information, see Perform HLS encryption and play an encrypted video.

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.mps.sdk.utils.InitClient;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyRequest;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyResponse;
import com.aliyuncs.mts.model.v20140618.SubmitJobsRequest;
import com.aliyuncs.mts.model.v20140618.SubmitJobsResponse;
import org.apache.commons.codec.binary.Base64;

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

/**
 * *****   Usage notes     ******
 * This sample code performs HLS encryption by using KMS. You must decrypt an encrypted video before you play it. For more information, see the "Sample code for decryption by using KMS" section of this topic.
 * If you use KMS to perform HLS encryption for the first time, create a workflow for HLS encryption.
 * The KeyId parameter specifies the key generated by KMS. A key is automatically created when you run a workflow for HLS encryption for the first time. You can view this key in the KMS console with an alias of alias/acs/mts.
 * The KeyUri parameter specifies the endpoint used to obtain the decryption key. Make sure that the specified key URI is valid. Otherwise, the decryption fails.
 */
public class KMSEncryptionTranscode {

    // The ID of the MPS queue. To view the ID of an MPS queue, log on to the MPS console and choose Global Settings > MPS Queue and Callback in the left-side navigation pane.
    private static String pipelineId = "b1a2b3c40a0cbf****";
    // The ID of the template. For more information about preset templates, see "Preset template details" at https://www.alibabacloud.com/help/zh/apsaravideo-for-media-processing/latest/yuzhimobanxiangqing.
    private static String templateId = "S00000001-100020";

    private DefaultAcsClient client = null;

    public KMSEncryptionTranscode() throws ClientException {

        this.client = InitClient.initMpsClient();
    }

    public static void main(String[] args) throws ClientException {
        KMSEncryptionTranscode kmsEncryptionTranscode = new KMSEncryptionTranscode();
        kmsEncryptionTranscode.submitJobs();
    }

    /**
     * Submit a transcoding job.
     * @throws ClientException
     */
    private void submitJobs() throws ClientException {

        JSONObject inputFile = new JSONObject();
        inputFile.put("Location", "oss-cn-beijing");
        inputFile.put("Bucket", "<your bucket name>"");
        try {
            inputFile.put("Object", URLEncoder.encode("mps-test/demo/test.mp4", "utf-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("input URL encode failed");
        }
        SubmitJobsRequest request = new SubmitJobsRequest();
        request.setInput(inputFile.toJSONString());
        request.setOutputLocation("oss-cn-beijing");
        request.setOutputBucket("bole-test0727");

        JSONArray outputs = new JSONArray();

        JSONObject output = new JSONObject();
        // The path of the output file. You do not need to specify the .m3u8 suffix for the output file. In this example, the hls-test.m3u8 file is generated.
        // Make sure that the output file does not trigger a workflow.
        output.put("OutputObject", "mps-test/demo-v2/hls-test");
        output.put("TemplateId", templateId);
        output.put("Encryption", getEncryptionConfigs());

        outputs.add(output);
        request.setOutputs(outputs.toJSONString());
        request.setPipelineId(pipelineId);

        SubmitJobsResponse response = client.getAcsResponse(request);
        System.out.println("RequestId is:" + response.getRequestId());
        System.out.println("JobId is:" + response.getJobResultList().get(0).getJob().getJobId());
        System.out.println("Response is:" + JSON.toJSONString(response));
    }

    /**
     * Build encryption configurations.
     * @return
     * @throws ClientException
     */
    private JSONObject getEncryptionConfigs() throws ClientException {
        GenerateDataKeyRequest generateDataKeyRequest = new GenerateDataKeyRequest();
        // Use the key with an alias of alias/acs/mts as the encryption key. You can view the key in the KMS console: https://kms.console.aliyun.com/cn-shanghai/key/list.
        generateDataKeyRequest.setKeyId("eebc***********eb3");
        generateDataKeyRequest.setKeySpec("AES_128");
        GenerateDataKeyResponse generateDataKeyResponse = client.getAcsResponse(generateDataKeyRequest);
        JSONObject encryption = new JSONObject();
        encryption.put("Type", "hls-aes-128");
        encryption.put("Key", generateDataKeyResponse.getCiphertextBlob());
        String url = "http://127.0.0.1:8888?Ciphertext=" + generateDataKeyResponse.getCiphertextBlob(); // The URL used to obtain the decryption key. Specify this parameter based on your business requirements.
        encryption.put("KeyUri", Base64.encodeBase64URLSafeString(url.getBytes()));
        encryption.put("KeyType", "KMS");
        return encryption;
    }
}

Sample code for decryption by using the Base64 algorithm

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;

/**
 * *****   Usage notes     ******
 * This sample code decodes the data key (DK) by using the Base64 algorithm.
 * The port number in the sample code is 8888. Make sure that this number is the same as that of the key URI.
 * If you need additional token verification, see the following sample code for decryption by using KMS.
 *
 * ***** Code logic ******
 * 1. Receive the decryption request and obtain the enveloped data key (EDK).
 * 2. Decode the DK by using the Base64 algorithm and return the decoded DK.
 */
public class Base64DecryptServer {

    public static void main(String[] args) throws IOException {
        Base64DecryptServer server = new Base64DecryptServer();
        server.startService();
    }

    public class Base64DecryptHandler 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)) {
                // The decryption key must be the same as the encryption key.
                byte[] key = "encryptionkey128".getBytes();
                // Configure the headers.
                setHeader(httpExchange, key);
                // Return the DK that is decoded by using the Base64 algorithm.
                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);
        }
    }
    /**
     * Start the service.
     * @throws IOException
     */
    private void startService() throws IOException {
        HttpServerProvider provider = HttpServerProvider.provider();
        // Configure a listener on port 8888, which can accept 30 requests at a time.
        HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8888), 30);
        httpserver.createContext("/", new Base64DecryptHandler());
        httpserver.start();
        System.out.println("base64 hls decrypt server started");
    }

}

Sample code for decryption by using 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;

/**
 * *****   Usage notes     ******
 * This sample code constructs the decryption service by using KMS with the M3U8 encryption and rewrite feature disabled. The MtsHlsUriToken parameter is not verified.
 * For more information about the M3U8 encryption and rewrite feature and how to generate the MtsHlsUriToken parameter, see xxxxxxxxxxxx.
 * The port number in the sample code is 8888. Make sure that this number is the same as that of the key URI.
 *
 * ***** Code logic ******
 * 1. Receive the decryption request and obtain the EDK.
 * 2. Call the Decrypt operation of KMS to obtain the DK. 
 * 3. Decode the DK by using the Base64 algorithm and return the decoded DK.
 */
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")){
                // Obtain the EDK from the video URL.
                String ciphertext = getCiphertext(httpExchange);
                System.out.println(ciphertext);
                if (null == ciphertext)
                    return;
                // Decrypt the DK obtained from KMS and decode the DK by using the Base64 algorithm.
                byte[] key = decrypt(ciphertext);
                // Configure the headers.
                setHeader(httpExchange, key);
                // Return the decoded 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);
        }

        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 DK by using the Base64 algorithm.
                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;
            }
        }
    }

    /**
     * Start the service.
     *
     * @throws IOException
     */
    private void startService() throws IOException {
        HttpServerProvider provider = HttpServerProvider.provider();
        // Configure a listener on port 8888, which can accept 30 requests at a time. You can change the port based on your business requirements.
        HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8888), 30);
        httpserver.createContext("/", new HlsDecryptHandler());
        httpserver.start();
        System.out.println("no token hls decrypt server started");
    }

}