Apple FairPlay“遇到解密密钥错误。” JavaScript

时间:2019-06-05 06:34:56

标签: javascript fairplay

我正在尝试使用SDK中提供的safari示例使Apple FairPlay正常工作,但出现错误A decryption key error was encountered.

我正在使用发现的here(用go编写)的密钥服务器模块,如果我向其发送示例http post请求,该模块将起作用。我也使用了SDK中提供的默认开发密钥。

我有一个dev_certificate.der文件托管在我的本地主机中,还有一个示例视频文件master.m3u8(我是从Apple FPS测试内容中获得的)。

我的问题是:我遇到什么错误? (这是什么意思,等等)我是否正确设置了HTML文件?我是否需要与仅托管文件不同地托管证书?很感谢任何形式的帮助。

我只是试图简单地使用FairPlay来运行无法在Safari中进行屏幕截图的视频。

我认为这与我的服务器端设置有关,因为它在使用videojs FairPlay时也不起作用。 (相同的错误)

这是我的html文件的当前代码(我没有更改示例文件中的任何内容,但更改了文件的路径)。

<!DOCTYPE html>
<html>
    <!--  Portions Copyright (C) 2015 Apple Inc. All Rights Reserved.
     See the Apple Developer Program License Agreement for this file's licensing information.
     All use of these materials is subject to the terms of the Apple Developer Program License Agreement.
     -->

    <!-- Usage Notes:
        At least 4 adjustments need to be made for functionality and to meet the needs of the integrator:
         a) update path to your FPS certificate
         b) update path to your Key Security Module (KSM)
         c) update for the SPC extraction from the POST according to your client protocol
         d) update for the m3u8 URL lookup and communication in the response to the client

        The sections requiring tweaking are marked with the "ADAPT" comment in the code below.
    -->

    <head>
        <title>FPS Template</title>
        <script>

        /*
            The EME specification (https://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/encrypted-media.html)
            is supported starting OSX 10.10 and greater.
        */
        var keySystem;
        var certificate;
        var serverCertificatePath = 'http://localhost/dev_certificate.der'; // ADAPT: This is the path to the fps certificate on your server.
        var serverProcessSPCPath  = 'http://localhost:8080/fps/rest/getLicense';     // ADAPT: This is the path/URL to the keyserver module that processes the SPC and returns a CKC

        function stringToArray(string) {
            var buffer = new ArrayBuffer(string.length*2); // 2 bytes for each char
            var array = new Uint16Array(buffer);
            for (var i=0, strLen=string.length; i<strLen; i++) {
                array[i] = string.charCodeAt(i);
            }
            return array;
        }

        function arrayToString(array) {
            var uint16array = new Uint16Array(array.buffer);
            return String.fromCharCode.apply(null, uint16array);
        }

        function base64DecodeUint8Array(input) {
            var raw = window.atob(input);
            var rawLength = raw.length;
            var array = new Uint8Array(new ArrayBuffer(rawLength));

            for(i = 0; i < rawLength; i++)
                array[i] = raw.charCodeAt(i);

            return array;
        }

        function base64EncodeUint8Array(input) {
            var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
            var output = "";
            var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            var i = 0;

            while (i < input.length) {
                chr1 = input[i++];
                chr2 = i < input.length ? input[i++] : Number.NaN; // Not sure if the index
                chr3 = i < input.length ? input[i++] : Number.NaN; // checks are needed here

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }
                output += keyStr.charAt(enc1) + keyStr.charAt(enc2) +
                    keyStr.charAt(enc3) + keyStr.charAt(enc4);
            }
            return output;
        }

        function waitForEvent(name, action, target) {
            target.addEventListener(name, function() {
                    action(arguments[0]);
                    }, false);
        }

        function loadCertificate()
        {
            var request = new XMLHttpRequest();
            request.responseType = 'arraybuffer';
            request.addEventListener('load', onCertificateLoaded, false);
            request.addEventListener('error', onCertificateError, false);
            request.open('GET', serverCertificatePath, true);
            request.setRequestHeader('Pragma', 'Cache-Control: no-cache');
            request.setRequestHeader("Cache-Control", "max-age=0");
            request.send();
        }

        function onCertificateLoaded(event) {
            var request = event.target;
            certificate = new Uint8Array(request.response);
            startVideo();
        }

        function onCertificateError(event) {
            window.console.error('Failed to retrieve the server certificate.')
        }

        function extractContentId(initData) {
            contentId = arrayToString(initData);
            // contentId is passed up as a URI, from which the host must be extracted:
            var link = document.createElement('a');
            link.href = contentId;
            return link.hostname;
        }

        function concatInitDataIdAndCertificate(initData, id, cert) {
            if (typeof id == "string")
                id = stringToArray(id);
            // layout is [initData][4 byte: idLength][idLength byte: id][4 byte:certLength][certLength byte: cert]
            var offset = 0;
            var buffer = new ArrayBuffer(initData.byteLength + 4 + id.byteLength + 4 + cert.byteLength);
            var dataView = new DataView(buffer);

            var initDataArray = new Uint8Array(buffer, offset, initData.byteLength);
            initDataArray.set(initData);
            offset += initData.byteLength;

            dataView.setUint32(offset, id.byteLength, true);
            offset += 4;

            var idArray = new Uint16Array(buffer, offset, id.length);
            idArray.set(id);
            offset += idArray.byteLength;

            dataView.setUint32(offset, cert.byteLength, true);
            offset += 4;

            var certArray = new Uint8Array(buffer, offset, cert.byteLength);
            certArray.set(cert);

            return new Uint8Array(buffer, 0, buffer.byteLength);
        }

        function selectKeySystem()
        {
            if (WebKitMediaKeys.isTypeSupported("com.apple.fps.1_0", "video/mp4"))
            {
                keySystem = "com.apple.fps.1_0";
            }
            else
            {
                throw "Key System not supported";
            }
        }

        function startVideo()
        {
            var video = document.getElementsByTagName('video')[0];
            video.addEventListener('webkitneedkey', onneedkey, false);
            video.addEventListener('error', onerror, false);
            // ADAPT: there must be logic here to fetch/build the appropriate m3u8 URL
            video.src = 'http://localhost/master.m3u8';
        }

        function onerror(event) {
            window.console.error('A video playback error occurred')
        }

        function onneedkey(event)
        {
            var video = event.target;
            var initData = event.initData;
            var contentId = extractContentId(initData);
            initData = concatInitDataIdAndCertificate(initData, contentId, certificate);

            if (!video.webkitKeys)
            {
                selectKeySystem();
                video.webkitSetMediaKeys(new WebKitMediaKeys(keySystem));
            }

            if (!video.webkitKeys)
                throw "Could not create MediaKeys";

            var keySession = video.webkitKeys.createSession("video/mp4", initData);
            if (!keySession)
                throw "Could not create key session";

            keySession.contentId = contentId;
            waitForEvent('webkitkeymessage', licenseRequestReady, keySession);
            waitForEvent('webkitkeyadded', onkeyadded, keySession);
            waitForEvent('webkitkeyerror', onkeyerror, keySession);
        }

        /*
            This function assumes the Key Server Module understands the following POST format --
            spc=<base64 encoded data>&assetId=<data>
            ADAPT: Partners must tailor to their own protocol.
        */
        function licenseRequestReady(event)
        {
            var session = event.target;
            var message = event.message;
            var request = new XMLHttpRequest();
            var sessionId = event.sessionId;
            request.responseType = 'text';
            request.session = session;
            request.addEventListener('load', licenseRequestLoaded, false);
            request.addEventListener('error', licenseRequestFailed, false);
            var params = 'spc='+base64EncodeUint8Array(message)+'&assetId='+encodeURIComponent(session.contentId);
            request.open('POST', serverProcessSPCPath, true);
            request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            request.send(params);
        }

        function licenseRequestLoaded(event)
        {
            var request = event.target;
            var session = request.session;
            // response can be of the form: '\n<ckc>base64encoded</ckc>\n'
            // so trim the excess:
            keyText = request.responseText.trim();
            if (keyText.substr(0, 5) === '<ckc>' && keyText.substr(-6) === '</ckc>')
                keyText = keyText.slice(5,-6);
            key = base64DecodeUint8Array(keyText);
            session.update(key);
        }

        function licenseRequestFailed(event)
        {
            window.console.error('The license request failed.');
        }

        function onkeyerror(event) {
            window.console.error('A decryption key error was encountered');
        }

        function onkeyadded(event) {
            window.console.log('Decryption key was added to session.');
        }

        </script>
    </head>
    <body onload="loadCertificate()">
        <video controls autoplay preload="auto" width=960></video>
    </body>
</html>

如果您需要我提供其他任何信息,我非常乐意提供。

0 个答案:

没有答案