HLS data encryption
Video encryption secures video content to prevent illegal access, distribution, video leaks, and hotlinking. It is widely used in industries that require high content security, such as online education and finance. Alibaba Cloud supports two encryption methods: Alibaba Cloud proprietary cryptography (recommended) and HLS encryption. This topic describes the principles of and procedures for using HLS encryption in ApsaraVideo Media Processing. This helps you implement HLS encryption to ensure video security and a smooth online playback experience.
Scenarios
HLS encryption protects your videos from illegal downloads and distribution.
If you have high security requirements, you can enable data encryption in your workflow. For more information, see Data encryption.
Usage notes
HLS encryption requires the use of the SubmitJobs API operation.
You cannot use the Base64 method for HLS encryption in a workflow.
For information about the principles of standard encryption, see HLS encryption.
Sample code dependencies
For more information about the ApsaraVideo Media Processing (MPS) software development kit (SDK), see Installation.
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>
Encrypt using KMS (Recommended)
Encrypt and transcode
If you use a workflow to trigger standard encryption and transcoding, you can trigger the workflow by re-uploading the file to the specified input path or using the AddMedia operation.
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 {
/**
* description :
* <p>Use an 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 set.
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is set.
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
config.endpoint = "mts.cn-shanghai.aliyuncs.com";
return new com.aliyun.mts20140618.Client(config);
}
/**
* description :
* <p>Use an 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 set.
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is set.
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
config.endpoint = "kms.cn-shanghai.aliyuncs.com";
return new com.aliyun.kms20160120.Client(config);
}
/**
* Submit a KMS encryption and transcoding job.
* @return
* @throws Exception
*/
public static void submitJobsForKMS() throws Exception {
// Obtain the ciphertextBlob.
JSONObject generateDataKey = getEncryptionConfigs();
String ciphertextBlob = generateDataKey.getString("ciphertextBlob");
System.out.println(ciphertextBlob);
// Build the 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 decryption URL. Configure it as needed.
encryption.put("KeyUri", Base64.encodeBase64URLSafeString(url.getBytes()));
encryption.put("KeyType", "KMS");
// Build the input. Make sure that the Location region is the same as the service client region.
JSONObject input = new JSONObject();
input.put("Bucket", "<your bucket name>");
input.put("Location", "oss-cn-shanghai");
// Build an output object.
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 a standard encryption and transcoding job.
com.aliyun.mts20140618.Client client = SubmitJobsForKMS.createMTSClient();
com.aliyun.mts20140618.models.SubmitJobsRequest submitJobsRequest = new com.aliyun.mts20140618.models.SubmitJobsRequest()
// Job input
.setInput(input.toJSONString())
// Job output configurations
.setOutputs(outputs.toJSONString())
// The OSS bucket where the output file is stored.
.setOutputBucket("<your bucket name>")
// The region of the OSS bucket where the output file is stored.
.setOutputLocation("oss-cn-shanghai")
// MPS queue ID
.setPipelineId("<transcode pipelineId>");
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// If you copy the code, print the return value of the API operation.
client.submitJobsWithOptions(submitJobsRequest, runtime);
} catch (TeaException error) {
// This is for demonstration purposes only. Handle exceptions with caution. Do not ignore exceptions in your project.
// Error message
System.out.println(error.getMessage());
// Diagnostic address
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// This is for demonstration purposes only. Handle exceptions with caution. Do not ignore exceptions in your project.
// Error message
System.out.println(error.getMessage());
// Diagnostic address
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
}
}
/**
* Request encryption configurations from 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();
// Replace this with the key that has the alias alias/acs/mts in the KMS console.
generateDataKeyRequest.setKeyId("3f27f*************f");
generateDataKeyRequest.setKeySpec("AES_128");
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// If you copy the code, print the return value of the API operation.
com.aliyun.kms20160120.models.GenerateDataKeyResponse generateDataKeyResponse = client.generateDataKeyWithOptions(generateDataKeyRequest, runtime);
return JSONObject.parseObject(JSON.toJSONString(generateDataKeyResponse.getBody()));
} catch (TeaException error) {
// This is for demonstration purposes only. Handle exceptions with caution. Do not ignore exceptions in your project.
// Error message
System.out.println(error.getMessage());
// Diagnostic address
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);
// This is for demonstration purposes only. Handle exceptions with caution. Do not ignore exceptions in your project.
// Error message
System.out.println(error.getMessage());
// Diagnostic address
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 URL.
String ciphertext = getCiphertext(httpExchange);
System.out.println(ciphertext);
if (null == ciphertext)
return;
// Decrypt the key from KMS and perform Base64 decoding.
byte[] key = decrypt(ciphertext);
// Set the header.
setHeader(httpExchange, key);
// Return the 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();
// Replace this with the key that has the alias alias/acs/mts in the KMS console.
decryptRequest.setCiphertextBlob(ciphertext);
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
// If you copy the code, print the return value of the API operation.
com.aliyun.kms20160120.models.DecryptResponse decryptResponse = client.decryptWithOptions(decryptRequest, runtime);
String plaintext = decryptResponse.getBody().getPlaintext();
return Base64.decodeBase64(plaintext);
} catch (TeaException error) {
// This is for demonstration purposes only. Handle exceptions with caution. Do not ignore exceptions in your project.
// Error message
System.out.println(error.getMessage());
// Diagnostic address
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);
// This is for demonstration purposes only. Handle exceptions with caution. Do not ignore exceptions in your project.
// Error message
System.out.println(error.getMessage());
// Diagnostic address
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();
// Listen on port 8888. It can accept 30 requests at the same time. You can change the port as needed.
HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8888), 30);
httpserver.createContext("/", new HlsDecryptHandler());
httpserver.start();
System.out.println("no token hls decrypt server started");
}
/**
* description :
* <p>Use an 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 set.
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is set.
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
config.endpoint = "kms.cn-shanghai.aliyuncs.com";
return new com.aliyun.kms20160120.Client(config);
}
}
Encrypt using the Base64 method
Encrypt and transcode
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 {
/**
* description :
* <p>Use an 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 set.
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
// Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is set.
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
config.endpoint = "mts.cn-shanghai.aliyuncs.com";
return new com.aliyun.mts20140618.Client(config);
}
/**
* Build the encryption configuration.
* @return
*/
private static JSONObject getEncryptionConfigs() throws Exception {
JSONObject encryption = new JSONObject();
encryption.put("Type", "hls-aes-128");
// The encryption string must be 16 bytes.
encryption.put("Key", Base64.encodeBase64URLSafeString("encryptionkey128".getBytes()));
// The URL is your decryption service address.
String url = "http://127.0.0.1:8888";
encryption.put("KeyUri", Base64.encodeBase64URLSafeString(url.getBytes()));
encryption.put("KeyType", "Base64");
return encryption;
}
/**
* Submit a Base64 encryption and transcoding job.
* @return
* @throws Exception
*/
public static void submitJobsForBase64() throws Exception {
// Build the input. Make sure that the Location region is the same as the service client region.
JSONObject input = new JSONObject();
input.put("Bucket", "<your bucket name>");
input.put("Location", "oss-cn-shanghai");
// Build an output object.
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 a standard encryption and transcoding job.
com.aliyun.mts20140618.Client client = SubmitJobsForKMS.createMTSClient();
com.aliyun.mts20140618.models.SubmitJobsRequest submitJobsRequest = new com.aliyun.mts20140618.models.SubmitJobsRequest()
// Job input
.setInput(input.toJSONString())
// Job output configurations
.setOutputs(outputs.toJSONString())
// The OSS bucket where the output file is stored.
.setOutputBucket("<your bucket name>")
// The region of the OSS bucket where the output file is stored.
.setOutputLocation("oss-cn-shanghai")
// MPS queue ID
.setPipelineId("<transcode pipelineId>");
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// If you copy the code, print the return value of the API operation.
client.submitJobsWithOptions(submitJobsRequest, runtime);
} catch (TeaException error) {
// This is for demonstration purposes only. Handle exceptions with caution. Do not ignore exceptions in your project.
// Error message
System.out.println(error.getMessage());
// Diagnostic address
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// This is for demonstration purposes only. Handle exceptions with caution. Do not ignore exceptions in your project.
// Error message
System.out.println(error.getMessage());
// Diagnostic address
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 {
/**
* Handle 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 here must be the same as the encryption key.
byte[] key = "encryptionkey128".getBytes();
// Set the header.
setHeader(httpExchange, key);
// Return the key after Base64 decoding.
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();
// Listen on port 8888. It can accept 30 requests at the same time.
HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8888), 30);
httpserver.createContext("/", new Base64DecryptHandler());
httpserver.start();
System.out.println("base64 hls decrypt server started");
}
}