開發與維運

视频点播视频加密流程解决方案

业务场景
image.png

当前阿里云视频点播服务有两种加密方式:1、阿里云私有加密;(目前移动端H5兼容性不好,H5仅支持Android的Chrome浏览器)2、HLS标准加密;(支持H5)
移动端以及PC兼容情况:
image.png
解决方案
1、阿里云私有加密
1)加密文件生成,转码模板处勾选“HLS加密”即可。
image.png
生成之后的加密文件是下面的样式:
image.png
同样也可以通过curl去查看您的视频是否是阿里云私有加密,curl出来的结果是URI=xxx则为阿里云私有加密视频
image.png









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.html
2、HLS标准加密
1)生成HLS标准加密视频,目前生成HLS标准加密的视频只能通过API接口或者SDK进行处理。(也就是只能代码生成,无法直接使用控制台)
逻辑主要是:
image.png
1、创建加密转码模板
image.png
2、RAM授权
使用RAM服务给视频点播授权访问业务方秘钥管理服务(KMS)的权限
image.png
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.html
2)直接使用提交转码作业是SDK demo(推荐使用
以Java为例子:https://help.aliyun.com/document_detail/98672.html
只需要确认AK、SK、Service Key以及DecryptKeyUri正确即可
最终生成的HLS标准加密的视频:
image.png
通过curl进行验证,得到的URI是http://解密服务地址/xxx?Ciphertext=xxx 这样的连接,则表示加密成功了。
image.png
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的时候,带上这个参数。
image.png
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();
}

}

Leave a Reply

Your email address will not be published. Required fields are marked *