The video encryption feature allows you to perform in-depth security processing on video content and prevent video data from being illegally acquired and transmitted. This feature is widely used to prevent video leaks and hotlinking in scenarios that require high security, such as online education and finance. ApsaraVideo Media Processing (MPS) supports two encryption methods: Alibaba Cloud proprietary cryptography and HTTP-Live-Streaming (HLS) encryption. This topic describes how HLS encryption works and how to use HLS encryption. This helps you ensure video security by using HLS encryption and smoothly play videos.
Scenarios
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.
For information about the technical principles of HLS encryption, see HLS encryption.
Dependencies
Install MPS SDK for Java. For more information, see Install MPS SDK for Java.
Add other dependencies.
<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-based encryption (recommended)
Encryption and transcoding
If you use a workflow to trigger HLS encryption and transcoding, you can upload the file to the input path specified by the workflow. You can also call AddMedia to trigger the workflow.
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>Use your AccessKey pair to initialize the 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()
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable is configured.
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is configured.
.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>Use your AccessKey pair to initialize the MPS 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()
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable is configured.
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is configured.
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
config.endpoint = "kms.cn-shanghai.aliyuncs.com";
return new com.aliyun.kms20160120.Client(config);
}
/**
* Submit KMS-based encryption and transcoding.
* @return
* @throws Exception
*/
public static void submitJobsForKMS() throws Exception {
// Obtain ciphertextBlob.
JSONObject generateDataKey = getEncryptionConfigs();
String ciphertextBlob = generateDataKey.getString("ciphertextBlob");
System.out.println(ciphertextBlob);
// Create encryption parameters.
JSONObject encryption = new JSONObject();
encryption.put("Type", "hls-aes-128");
encryption.put("Key", ciphertextBlob);
String url = "http://127.0.0.1:8888?Ciphertext=" + ciphertextBlob; // The URL that is 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");
// Configure the job input. Make sure that the media file used as the job input resides in the same region as the client.
JSONObject input = new JSONObject();
input.put("Bucket", "<your bucket name>");
input.put("Location", "oss-cn-shanghai");
// Configure the job output.
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);
// Submit HLS encryption and transcoding.
com.aliyun.mts20140618.Client client = SubmitJobsForKMS.createMTSClient();
com.aliyun.mts20140618.models.SubmitJobsRequest submitJobsRequest = new com.aliyun.mts20140618.models.SubmitJobsRequest()
// The job input.
.setInput(input.toJSONString())
// The job output configuration.
.setOutputs(outputs.toJSONString())
// The OSS bucket that stores the output file.
.setOutputBucket("<your bucket name>")
// The region in which the OSS bucket resides.
.setOutputLocation("oss-cn-shanghai")
// The ID of the MPS queue.
.setPipelineId("<transcode pipelineId>");
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// Write your own code to display the response of the API operation if necessary.
client.submitJobsWithOptions(submitJobsRequest, runtime);
} catch (TeaException error) {
// Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided for reference only.
// The error message.
System.out.println(error.getMessage());
// The URL for troubleshooting.
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided only for reference.
// The error message.
System.out.println(error.getMessage());
// The URL for troubleshooting.
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
}
}
/**
* Request KMS to obtain the encryption configuration.
* @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();
// Use the key with an alias of alias/acs/mts as the encryption key.
generateDataKeyRequest.setKeyId("3f27f*************f");
generateDataKeyRequest.setKeySpec("AES_128");
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// Write your own code to display the response of the API operation if necessary.
com.aliyun.kms20160120.models.GenerateDataKeyResponse generateDataKeyResponse = client.generateDataKeyWithOptions(generateDataKeyRequest, runtime);
return JSONObject.parseObject(JSON.toJSONString(generateDataKeyResponse.getBody()));
} catch (TeaException error) {
// Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided for reference only.
// The error message.
System.out.println(error.getMessage());
// The URL for troubleshooting.
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);
// Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided for reference only.
// The error message.
System.out.println(error.getMessage());
// The URL for troubleshooting.
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();
}
}
Decryption service
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")){
// Obtain the ciphertext key 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 header.
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) {
try {
com.aliyun.kms20160120.Client client = DecryptServerKMS.createKMSClient();
com.aliyun.kms20160120.models.DecryptRequest decryptRequest = new com.aliyun.kms20160120.models.DecryptRequest();
// Use the key with an alias of alias/acs/mts as the encryption key.
decryptRequest.setCiphertextBlob(ciphertext);
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
// Write your own code to display the response of the API operation if necessary.
com.aliyun.kms20160120.models.DecryptResponse decryptResponse = client.decryptWithOptions(decryptRequest, runtime);
String plaintext = decryptResponse.getBody().getPlaintext();
return Base64.decodeBase64(plaintext);
} catch (TeaException error) {
// Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided for reference only.
// The error message.
System.out.println(error.getMessage());
// The URL for troubleshooting.
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);
// Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided for reference only.
// The error message.
System.out.println(error.getMessage());
// The URL for troubleshooting.
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;
}
}
}
/**
* 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");
}
/**
* <b>description</b> :
* <p>Use your AccessKey pair to initialize the 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()
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable is configured.
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is configured.
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
config.endpoint = "kms.cn-shanghai.aliyuncs.com";
return new com.aliyun.kms20160120.Client(config);
}
}
Base64 encryption
Encryption and transcoding
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>Use your AccessKey pair to initialize the 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()
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable is configured.
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is configured.
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
config.endpoint = "mts.cn-shanghai.aliyuncs.com";
return new com.aliyun.mts20140618.Client(config);
}
/**
* Build encryption configurations.
* @return
*/
private static JSONObject getEncryptionConfigs() throws Exception {
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;
}
/**
* Submit KMS encryption and transcoding.
* @return
* @throws Exception
*/
public static void submitJobsForBase64() throws Exception {
// Configure the job input. Make sure that the media file used as the job input resides in the same region as the client.
JSONObject input = new JSONObject();
input.put("Bucket", "<your bucket name>");
input.put("Location", "oss-cn-shanghai");
// Configure the job output.
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);
// Submit HLS encryption and transcoding.
com.aliyun.mts20140618.Client client = SubmitJobsForKMS.createMTSClient();
com.aliyun.mts20140618.models.SubmitJobsRequest submitJobsRequest = new com.aliyun.mts20140618.models.SubmitJobsRequest()
// The job input.
.setInput(input.toJSONString())
// The job output configuration.
.setOutputs(outputs.toJSONString())
// The OSS bucket that stores the output file.
.setOutputBucket("<your bucket name>")
// The region in which the OSS bucket resides.
.setOutputLocation("oss-cn-shanghai")
// The ID of the MPS queue.
.setPipelineId("<transcode pipelineId>");
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// Write your own code to display the response of the API operation if necessary.
client.submitJobsWithOptions(submitJobsRequest, runtime);
} catch (TeaException error) {
// Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided for reference only.
// The error message.
System.out.println(error.getMessage());
// The URL for troubleshooting.
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided for reference only.
// The error message.
System.out.println(error.getMessage());
// The URL for troubleshooting.
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
}
}
public static void main(String[] args) throws Exception{
submitJobsForBase64();
}
}
Decryption service
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 {
/**
* 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 plaintext key 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");
}
}