業務場景

目前阿裡雲視訊點播服務有兩種加密方式:1、阿裡雲私有加密;(目前移動端H5相容性不好,H5僅支援Android的Chrome浏覽器)2、HLS标準加密;(支援H5)
移動端以及PC相容情況:
解決方案
1、阿裡雲私有加密
1)加密檔案生成,轉碼模闆處勾選“HLS加密”即可。
2)阿裡雲私有加密視訊播放,私有加密的播放需要滿足兩個條件:1、使用阿裡雲提供的播放器;(目前有web、Android以及iOS)2、使用videoID+STS或者videoID+playauth方式播放。
web demo:
https://player.alicdn.com/aliplayer/setting/setting.html android以及iOS demo: https://help.aliyun.com/document_detail/51992.html2、HLS标準加密
1)生成HLS标準加密視訊,目前生成HLS标準加密的視訊隻能通過API接口或者SDK進行處理。(也就是隻能代碼生成,無法直接使用控制台)
邏輯主要是:
2、RAM授權
使用RAM服務給視訊點播授權通路業務方秘鑰管理服務(KMS)的權限
3、建立Service Key(KMS控制台建立即可)
4、送出加密轉碼:
1)通過接口:GenerateDataKey生成明文秘鑰和密文秘鑰之後,再送出轉碼作業;(EncryptConfig裡寫HLS标準加密資訊)
GenerateDataKey:
https://help.aliyun.com/document_detail/28948.html 送出轉碼作業接口: https://help.aliyun.com/document_detail/68570.html2)直接使用送出轉碼作業是SDK demo(推薦使用)
以Java為例子:
https://help.aliyun.com/document_detail/98672.html隻需要确認AK、SK、Service Key以及DecryptKeyUri正确即可
最終生成的HLS标準加密的視訊:
5、解密HLS标準加密視訊
1、談到解密,大家會比較關心一個令牌HlsMtsToken,這個其實是一個附件的選項。(可有可無)舉個例子:
我生成的加密位址是:
https://vod.xxxx.cn/8e1f0d9295cf41989a837f4aaab7a813/87ba78922d59fe6cba843e48b5ccb659-hd-encrypt-stream.m3u8 如果我的伺服器需要需要有個校驗值的話,就可以寫成: https://vod.xxxx.cn/8e1f0d9295cf41989a837f4aaab7a813/87ba78922d59fe6cba843e48b5ccb659-hd-encrypt-stream.m3u8?MtsHlsUriToken=xxxx 注意:MtsHlsUriToken=xxxx這個是解密伺服器自己判斷進行校驗的,但是需要送出工單給阿裡雲的工程師,配置,請求解密URI的時候,帶上這個參數。2、解密伺服器搭建
以Java為例子,如果是其他語言的話,按照下述的步驟寫就好。
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 {
//KMS的區域,必須與視訊對應區域
String region = "<視訊對應區域>";
//通路KMS的授權AK資訊
String accessKeyId = "<您使用的AccessKeyId>";
String accessKeySecret = "<您使用的AccessKeySecrect>";
client = new DefaultAcsClient(DefaultProfile.getProfile(region, accessKeyId, accessKeySecret));
}
/**
* 說明:
* 1、接收解密請求,擷取密文秘鑰和令牌Token
* 2、調用KMS decrypt接口擷取明文秘鑰
* 3、将明文秘鑰base64decode傳回
*/
public class HlsDecryptHandler implements HttpHandler {
/**
* 處了解密請求
* @param httpExchange
* @throws IOException
*/
public void handle(HttpExchange httpExchange) throws IOException {
String requestMethod = httpExchange.getRequestMethod();
if ("GET".equalsIgnoreCase(requestMethod)) {
//校驗token的有效性
String token = getMtsHlsUriToken(httpExchange);
boolean validRe = validateToken(token);
if (!validRe) {
return;
}
//從URL中取得密文密鑰
String ciphertext = getCiphertext(httpExchange);
if (null == ciphertext)
return;
//從KMS中解密出來,并Base64 decode
byte[] key = decrypt(ciphertext);
//設定header
setHeader(httpExchange, key);
//傳回base64decode之後的密鑰
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);
}
/**
* 調用KMS decrypt接口解密,并将明文base64decode
* @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();
//注意:需要base64 decode
return Base64.decodeBase64(plaintext);
} catch (ClientException e) {
e.printStackTrace();
return null;
}
}
/**
* 校驗令牌有效性
* @param token
* @return
*/
private boolean validateToken(String token) {
if (null == token || "".equals(token)) {
return false;
}
//TODO 業務方實作令牌有效性校驗
return true;
}
/**
* 從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;
}
}
/**
* 擷取Token參數
*
* @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;
}
}
}
/**
* 服務啟動
*
* @throws IOException
*/
private void serviceBootStrap() throws IOException {
HttpServerProvider provider = HttpServerProvider.provider();
//監聽端口9999,能同時接受30個請求
HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(9999), 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();
}
}