加密的 mp3 流式传输到 Flash SWF
Posted
技术标签:
【中文标题】加密的 mp3 流式传输到 Flash SWF【英文标题】:Encrypted mp3 streaming to Flash SWF 【发布时间】:2013-01-13 00:14:56 【问题描述】:我有一个为客户端 Flash Player (SWF) 提供示例 MP3 文件的 asp.net 网站。
这些文件可通过大量下载工具下载。
虽然只有注册会员才能访问高质量的 mp3 样本,但我的客户希望防止这些低质量的 MP3 文件被下载工具下载。
所以我想到了这个解决方案:
-
在服务器端将这些 MP3 文件转换为字节数组 (ASP.NET)
做一些按位异或运算(简单加密)
将此数组写入 aspx 的响应流
修改 Flash (.fla) 以请求此新文件/页面/aspx
对 Flash 进行一些按位异或运算,并将其转换为原始 MP3 作为字节数组。 (简单解密)
播放 MP3
在第 6 步之前我能够成功。我无法将此字节数组转换为 Flash 可以播放的 Sound 对象。我对闪存上的结果数组和 ASP.NET 上的源数组进行了一点点比较。它们相等。
我对完全不同的方法持开放态度。但我不能使用 Flash Media Server。我需要使用 Flash as3 和 ASP.NET。
也很重要! .mp3 必须下载/解密并异步播放(我无法成功)
【问题讨论】:
存在预算限制(通常为零)。 【参考方案1】:@walkietokyo,非常感谢您为我指明了正确的方向。我成功地做了我想做的事。这里的关键字是loadCompressedDataFromByteArray
函数。
经过数十次反复试验,我发现loadCompressedDataFromByteArray
以不同的方式工作。
它附加任何它转换到声音对象的末尾数据。
另一个问题:在调用play
函数后,声音对象不会继续播放loadCompressedDataFromByteArray
附加的部分。
所以我实现了一种双缓冲。我可以互换使用 2 个声音对象。
下面列出了我的最终(测试)版本。使用我使用的加密(混淆)方法(一个简单的 XOR),我测试的任何下载管理器、抓取器或嗅探器都无法播放 Mp3。
Flash(客户端)端:
import flash.events.DataEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.OutputProgressEvent;
import flash.events.ProgressEvent;
import flash.net.URLRequest;
import flash.net.URLStream;
import flash.utils.ByteArray;
import flashx.textLayout.formats.Float;
var buffer:ByteArray;
var stream:URLStream;
var bufferReadPosition:uint = 0;
var bufferWritePosition:uint = 0;
var url:String = "http://www.blablabla.com/MusicServer.aspx?" + (new Date());
buffer = new ByteArray();
stream = new URLStream();
stream.addEventListener(ProgressEvent.PROGRESS, onProgress);
stream.load(new URLRequest(url));
var s1:Sound = new Sound();
var s2:Sound = new Sound();
var channel1:SoundChannel;
var channel2:SoundChannel;
var pausePosition:int = 0;
var aSoundIsPlaying:Boolean = false;
var lastLoadedS1:Boolean = false;
var lastS1Length:int = 0;
var lastS2Length:int = 0;
function onProgress(e:ProgressEvent):void
var tmpData:ByteArray = new ByteArray();
stream.readBytes(tmpData, 0, stream.bytesAvailable);
var decryptedData:ByteArray = decryptData(tmpData); // Decrypt loaded data
buffer.position = bufferWritePosition;
buffer.writeBytes(decryptedData, 0, decryptedData.length); // Add decrypted data to buffer
bufferWritePosition += decryptedData.length;
if(lastLoadedS1)
buffer.position = lastS2Length;
s2.loadCompressedDataFromByteArray(buffer, buffer.length - lastS2Length);
lastS2Length = buffer.length;
else
buffer.position = lastS1Length;
s1.loadCompressedDataFromByteArray(buffer, buffer.length - lastS1Length);
lastS1Length = buffer.length;
if(!aSoundIsPlaying)
DecidePlay();
function channel1Completed(e:Event):void
DecidePlay();
function channel2Completed(e:Event):void
DecidePlay();
function DecidePlay():void
aSoundIsPlaying = false;
if(lastLoadedS1)
channel1.stop();
if(s2.length - s1.length > 10000)
//At least a 10 second buffer
channel2 = s2.play(s1.length);
channel2.addEventListener(Event.SOUND_COMPLETE, channel2Completed);
lastLoadedS1 = false;
aSoundIsPlaying = true;
else
if(channel2 != null)
channel2.stop();
if(s1.length - s2.length > 10000)
//At least a 10 second buffer
channel1 = s1.play(s2.length);
channel1.addEventListener(Event.SOUND_COMPLETE, channel1Completed);
lastLoadedS1 = true;
aSoundIsPlaying = true;
function decryptData(data:ByteArray):ByteArray
for(var i:int = 0;i<data.length;i++)
//Here put in your bitwise decryption code
return data;
ASP.NET 服务器端(MusicServer.aspx):
protected void Page_Load(object sender, EventArgs e)
CopyStream(Mp3ToStream(Server.MapPath("blabla.mp3")), Response.OutputStream);
this.Response.AddHeader("Content-Disposition", "blabla.mp3");
this.Response.ContentType = "audio/mpeg";
this.Response.End();
public static void CopyStream(Stream input, Stream output)
byte[] buffer = new byte[32768];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
for (int i = 0; i < read; i++)
//Here put in your bitwise encryption code
output.Write(buffer, 0, read);
public Stream Mp3ToStream(string filePath)
using (FileStream fileStream = File.OpenRead(filePath))
MemoryStream memStream = new MemoryStream();
memStream.SetLength(fileStream.Length);
fileStream.Read(memStream.GetBuffer(), 0, (int)fileStream.Length);
return memStream;
【讨论】:
我知道这篇文章太旧了,但我需要这样的东西。你能分享一下这个的演示项目吗?【参考方案2】:我同意 Peter Elliot 的观点,即身份验证可能是限制对文件的访问的最简单方法。但是,如果您仍然需要探索加密文件的途径,我想我会扩展一点 Alex Vlad 的回答。
为了能够流式传输音频文件、即时解密和异步播放,您需要将URLStream
类 (docs) 与 Sound
类结合使用(docs) 并保留部分下载内容的缓冲区。
一些伪代码来说明:
class AsyncEncryptedSoundPlayer extends Sound
var buffer:ByteArray;
var stream:URLStream;
var currSoundPosition:uint = 0;
public function AsyncEncryptedSoundPlayer(url:String)
buffer = new ByteArray();
stream = new URLStream();
stream.addEventListener(ProgressEvent.PROGRESS, onProgress);
stream.load(new URLRequest(url));
addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleDataRequested);
function onProgress(e:ProgressEvent):void
var tmpData:ByteArray;
stream.readBytes(tmpData, buffer.length, stream.bytesAvailable - buffer.length);
var decryptedData:ByteArray = decryptData(tmpData); // Decrypt loaded data
buffer.writeBytes(decryptedData, buffer.length, decryptedData.length); // Add decrypted data to buffer
function onSampleDataRequested(e:ProgressEvent):void
// Feed samples from the buffer to the Sound instance
// You may have to pause the audio to increase the buffer it the download speed isn't high enough
event.data.writeBytes(buffer, currSoundPosition, 2048);
currSoundPosition += 2048;
function decryptedData(data:ByteArray):void
// Returns decrypted data
这显然是一个非常粗略的课程大纲,但我希望它能为您指明正确的方向。
【讨论】:
感谢 walkietokyo。这是我的第一次试验。 “使用纯 URLRequests”而不是使用 Sound.Load 的机制。但是我在将这些下载的部分流转换为可播放的部分(在内存中)MP3 时遇到了困难。您认为可以将@fsbmain 的解决方案与您的解决方案结合起来吗? @erich007 啊,是的。由于它是压缩音频文件,因此您还必须解码 MP3。您是否尝试过使用Sound.loadCompressedDataFromByteArray
方法? (link)【参考方案3】:
可能比加密从您的服务返回的数据更简单的方法是验证请求,以便只有您的 swf 可以请求文件。
您可以通过与 Amazon API 工作相同的方式来完成此操作:构建一个包含多个参数的请求,包括时间戳。将所有这些参数散列在一个 HMAC 中(HMAC-SHA256 在 as3crypto library 中可用)以及嵌入在您的 swf 中的私钥。您的服务器端对此请求进行身份验证,确保哈希有效并且与时间戳足够接近。任何具有错误哈希的请求,或使用时间戳过早的请求(重放攻击)都会被拒绝。
这当然不是完美的安全性。任何有足够动力的用户都可以反汇编您的 swf 并取出您的身份验证密钥,或者从他们的浏览器缓存中获取 mp3。但话又说回来,你要使用的任何机制都会有这些问题。这消除了必须加密和解密所有文件的开销,而是将工作转移到请求生成阶段。
【讨论】:
但是彼得,我不明白这如何解决我的问题。想想 youtube 下载工具...当您观看视频时,视频旁边会出现一个下载按钮,因为该工具会抓取原始数据。因此,即使我对 Flash 播放器进行身份验证,服务器也会发送一个干净的 mp3 作为响应。所以该工具将能够再次抓取它...... 这个想法是对请求进行身份验证,以防止外部工具能够从您的服务器发出数据请求。不会阻止用户从浏览器缓存中抓取它,但是您的 XOR 解决方案也不会真正阻止它。【参考方案4】:请看这里:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/SampleDataEvent.html
这里:
http://help.adobe.com/en_US/as3/dev/WSE523B839-C626-4983-B9C0-07CF1A087ED7.html
【讨论】:
过去 2 天检查、消化和测试了这些内容。这些链接无法帮助我“异步”下载和播放歌曲。【参考方案5】:Flash Sound
仅支持流式 mp3 播放,即您只能通过直接链接播放 mp3。但是您可以发送嵌入了 mp3 的 swf 文件,并且该 swf 可以像加密 mp3 一样被加密。
用于嵌入和使用mp3的as3代码:
public class Sounds
[Embed(source="/../assets/sounds/sound1.mp3")]
private static const sound1:Class;
通过Loader
加载此swf后,您可以通过以下方式访问声音:
var domain:ApplicationDomain = ApplicationDomain.currentDomain; // <-- ApplicationDomain where you load sounds.swf
var soundClass:Class = domain.getDefinition("Sounds_sound1");
var sound:Sound = new soundClass();
sound.play();
请确保您至少执行以下操作之一:
为声音类提供不同的名称 (sound1
)
为持有者类提供不同的名称 (Sounds
)
或将 sound.swf 加载到不同的应用程序域中
防止类名重叠。
不幸的是,这种方法不允许您流式播放声音,您必须加载整个 swf,解密它,然后才能播放声音。
【讨论】:
正如您也提到的,它不是异步的。我已经遇到过这个解决方案......谢谢以上是关于加密的 mp3 流式传输到 Flash SWF的主要内容,如果未能解决你的问题,请参考以下文章