Wowza secure Apple HTTP Live Streaming (AES-128 - external method). Player is not making the key request - aes

I have been working on Wowza Streaming Server and while trying to secure Apple HTTP Live Streaming using AES-128 - external method I am encountering below problems :
External AES-128 method of encryption is not working for .smil files present in the sub-folder of the application's source directory. I tried to achieve it by putting the [my-stream].key in [install-dir]/keys and [install-dir]/keys/[sub-folder-name] but both the scenarios failed for me to achieve this.
playlist url is :- [wowza-server-ip]:[port]/[application-name]/[applcation-instance-name]/smil:[sub-folder]/demo.smil/playlist.m3u8
In case of mp4s present in the application's source path, the player is not calling the key url.
The sequence of calls made by the player are :-
[wowza-server-ip]:[port]/crossdomain.xml
[wowza-server-ip]:[port]/[application-name]/[applcation-instance-name]/[stream-name]/playlist.m3u8
[wowza-server-ip]:[port]/[application-name]/[applcation-instance-name]/[stream-name]/chunklist_w[wowza-session-id].m3u8
[web-server-ip]:[port]/crossdomain.xml
After this player is not calling the "key request uri" as it was supposed to call. The calls are going properly when I am using the internal AES-128 method of Encryption.
My chunklist_w[wowza-session-id].m3u8 is
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:12
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-KEY:METHOD=AES-128,URI="http://[web-server-ip]:[port]/SimpleWebServlet/key.jsp?wowzasessionid=[session-id]"
#EXTINF:9.52,
media_w[session-id]_0.ts
#EXTINF:10.4,
media_w[session-id]_1.ts
[streamname].key file in [install-dir]/keys folder is
cupertinostreaming-aes128-key: DE51A7254739C0EDF1DCE13BBB308FF0
cupertinostreaming-aes128-url: http://[web-server-ip]:[port]/SimpleWebServlet/key.jsp
jsp file to return the key is key.jsp
<%# page import="java.util.*,java.io.*" %>
<%
boolean isValid = true;
if (!isValid)
{
response.setStatus( 403 );
}
else
{
response.setHeader("Content-Type", "binary/octet-stream");
response.setHeader("Pragma", "no-cache");
String keyStr = "DE51A7254739C0EDF1DCE13BBB308FF0";
int len = keyStr.length()/2;
byte[] keyBuffer = new byte[len];
for (int i=0;i<len;i++)
keyBuffer[i] = (byte)Integer.parseInt(keyStr.substring(i*2, (i*2)+2), 16);
OutputStream outs = response.getOutputStream();
outs.write(keyBuffer);
outs.flush();
}
%>
If anybody has encountered the similar problem or has successfully implemented the external aes-128 method of wowza, kindly put some light on the issues mentioned above.
EDIT 1
Kindly ignore the 2nd point as after further analysis I found out that there is some issue with the jboss delivering the key, once it delivers the crossdomain xml to the player.
For reference to this problem kindly check : Can I call two crossdomain.xml from two different servers from my flash player?
EDIT 2
Apologies for the typo in my first point. It should be .smil rather than .mp4, I have corrected the same in my first point

I recently tried out HLS with AES128 and it worked fine. My key file was in [wowzadir]/keys/mystream.key. Looks like it is your player that does not do something right here. Which player are you using?
You can try to use wget to download some chunks and you can inspect them with VLC for example to see if the encryption was applied.

Related

Attempting a Google Drive partial Download (Flutter) throws a header error

Here's my issue :
I am creating a small application based on audio files stored on Google Drive, in Flutter.
I am using the drive api to make my requests, with these scopes in my google sign in :
GoogleSignIn _googleSignIn = GoogleSignIn(
scopes: [
'email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/contacts.readonly',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/docs',
'https://www.googleapis.com/auth/drive.appdata',
],
);
I have an auth element and handle signing in and out. Until then, no issues.
I can also request my files with an implementation looking like this :
var api = widget.api.getAPI();
var files = await api.files.list($fields: '*');
This works perfectly, and so does :
var api = widget.api.getAPI();
var files = await api.files.get("myFileId"); (//does get a file instance)
But since I'd like to retrieve some of the Metadata included in my audio files, and since the drive API doesn't natively support extracting audio metadata and sending it as a google metadata, I thought I'd extract it with a partial download on the file itself.
Here's the catch : I can't seem to get the partial download to work.
Based on the doc, I thought the implementation would look something like this :
import 'package:googleapis/drive/v3.dart' as ga;
(...)
try {
var partiallyDownloadedFile = await api.files.get(
"myFileIdHere",
downloadOptions: ga.PartialDownloadOptions(ga.ByteRange(0, 10))); //should get a ga.Media instance
print("partial download succeeded");
print(partiallyDownloadedFile);
//(...do stuff...)
return;
} catch (err) {
print('Error occured : ');
print(err);
return;
}
But this always throws this error :
ApiRequestError(message: Attempting partial download but got invalid
'Content-Range' header (was: null, expected: bytes 0-10/).)
I tried using it on Wav files, but also MP4 files. The error is always the same, which leads me to believe it's my implementation that's somehow wrong, but I'm not sure what I'm supposed to do to fix it. Is it my request missing the header ? The response not including it ?
While very clear, that error doesn't help me troubleshoot my issue at all. I can't seem to find any documentation on how to conduct a partial media request. I haven't found any example projects to compare it with.
PartialDownloadOptions does not have much documentation.
I could handmake a partial request through the download links (which is how I can read the music to begin with) but the drive API supposedly allows this. Could anyone familiar with Flutter/the google APIs help me correct my implementation?
EDIT : This was due to an error within the commons library in the Dart google APIs, and was (at the very least superficially) fixed thanks to Kevmoo's efforts : https://github.com/google/googleapis.dart/issues/462
It was a Content-Range error happening due to browser specifications with access-control-expose-header compared to iOS/Android-type requests that typically expose every header.

Azure Media Service

Requirement: Upload video(s) on Azure portal. Also generate streaming URL after video encoding.
Code was written in Java as per this page found on their website - https://learn.microsoft.com/en-us/azure/media-services/media-services-java-how-to-use
but getting nothing in list when I try to retrieve list of AssetFileInfo
ListResult assetFiles = mediaService.list(AssetFile.list(asset.getAssetFilesLink()));
And due to this AssetFileInfo streamingAssetFile remains null and can't generate streaming URL as it is giving null pointer exception.
return originLocator.getPath() + streamingAssetFile.getName() + "/manifest";
Please assist. I am getting below error;
java.lang.NullPointerException
at com.zensar.azure.storage.blob.migration.MediaServices.getStreamingOriginLocator(MediaServices.java:212)
I just uploaded an mp4 file using the Azure portal. Then, I encoded the file and got the streaming URL as described in the article (https://learn.microsoft.com/en-us/azure/media-services/media-services-java-how-to-use).
The only change I made is commented out the following line:
// AssetInfo uploadAsset = uploadFileAndCreateAsset("BigBuckBunny.mp4");
And used this instead:
ListResult outputAssets = mediaService.list(Asset.list());

Error when uploading file with OpenMeetings' importFile REST method

I installed 3.0.3 OpenMeetings to test access via REST interface, everything worked ok for the methods in UserService and RoomService. But, when I try to upload a pdf file by ImportFile method (FileService), OpenMeetings returns an object FileImportError stating that the file is damaged, and that this may have occurred during the file transfer via http.
When I try to import the same file using the flex application of OpenMeetings everything works right. I'm using Ruby to call the method of ImportFile OpenMeeting, and to test whether my application is wrong, I called the method using Firefox and got the same error.
I am using the following method call (sample only, not the real ruby code):
ImportFile (SID externalUserId, externalFileId, externalType, room_id, isOwner, path,
parentFolderId, fileSystemName)
SID = one string with the ID of the session
externalUserId = 'extuser' (string)
externalType = 'exttype' (string)
room_id = 2 (existing room in OpenMeetings)
isOwner = false
path = 'http://10.1.1.25/default.pdf' (The path to the file on an Apache server)
parentFolderId = 0
fileSystemName = 'default.pdf'
Also used Eclipse in remote debug to see what was happening and realized that the problem occurs in the conversion of the received file.
I would appreciate some help to solve the problem.
Thanks,
Fernando

How to post a file in grails

I am trying to use HTTP to POST a file to an outside API from within a grails service. I've installed the rest plugin and I'm using code like the following:
def theFile = new File("/tmp/blah.txt")
def postBody = [myFile: theFile, foo:'bar']
withHttp(uri: "http://picard:8080/breeze/project/acceptFile") {
def html = post(body: postBody, requestContentType: URLENC)
}
The post works, however, the 'myFile' param appears to be a string rather than an actual file. I have not had any success trying to google for things like "how to post a file in grails" since most of the results end up dealing with handling an uploaded file from a form.
I think I'm using the right requestContentType, but I might have missed something in the documentation.
POSTing a file is not as simple as what you have included in your question (sadly). Also, it depends on what the API you are calling is expecting, e.g. some API expect files as base64 encoded text, while others accept them as mime-multipart.
Since you are using the rest plugin, as far as I can recall it uses the Apache HttpClient, I think this link should provide enough info to get you started (assuming you are dealing with mime-multipart). It shouldn't be too hard to change it around to work with your API and perhaps make it a bit 'groovy-ier'

Help with a Windows Service/Scheduled Task that must use a web browser and file dialogs

What I'm Trying To Do
I'm trying to create a solution of any kind that will run nightly on a Windows server, authenticate to a website, check a web page on the site for new links indicating a new version of a zip file, use new links (if present) to download a zip file, unzip the downloaded file to an existing folder on the server, use the unzipped contents (sql scripts, etc.) to build an instance of a database, and log everything that happens to a text file.
Forms App: The Part That Sorta Works
I created a Windows Forms app that uses a couple of WebBrowser controls, a couple of threads, and a few timers to do all that except the running nightly. It works great as a Form when I'm logged in and run it, but I need to get it (or something like it) to run on it's own like a Service or scheduled task.
My Service Attempt
So, I created a Windows Service that ticks every hour and, if the System.DateTime.Now.Hour >= 22, attempts to launch the Windows Forms app to do it's thing. When the Service attempts to launch the Form, this error occurs:
ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.
which I researched and tried to resolve by either placing the [STAThread] attribute on the Main method of the Service's Program class or using some code like this in a few places including the Form constructor:
webBrowseThread = new Thread(new ThreadStart(InitializeComponent));
webBrowseThread.SetApartmentState(ApartmentState.STA);
webBrowseThread.Start();
I couldn't get either approach to work. In the latter approach, the controls on the Form (which would get initialized inside IntializeComponent) don't get initialized and I get null reference exceptions.
My Scheduled Task Attempt
So, I tried creating a nightly scheduled task using my own credentials to run the Form locally on my dev machine (just testing). It gets farther than the Service did, but gets hung up at the File Download Dialog.
Related Note: To send the key sequences to get through the File Download and File Save As dialogs, my Form actually runs a couple of vbscript files that use WScript.Shell.SendKeys. Ok, that's embarassing to admit, but I tried a few different things including SendMessage in Win32 API and referencing IWshRuntimeLibrary to use SendKeys inside my C# code. When I was researching how to get through the dialogs, the Win32 API seemed to be the recommended way to go, but I couldn't figure it out. The vbscript files was the only thing I could get to work, but I'm worried now that this may be the reason why a scheduled task won't work.
Regarding My Choice of WebBrowser Control
I have read about the System.WebClient class as an alternative to the WebBrowser control, but at a glance, it doesn't look like it has what I need to get this done. For example, I needed (or I think I needed) the WebBrowser's DocumentCompleted and FileDownload events to handle the delays in pages loading, files downloading, etc. Is there more to WebClient that I'm not seeing? Is there another class besides WebBrowser that is more Service-friendly and would do the trick?
In Summary
Geez, this is long. Sorry! It would help to even have a high level recommendation for a better way to do what I'm trying to do, because nothing I've tried has worked.
Update 10/22/09
Well, I think I'm closer, but I'm stuck again. I should end up with a decent-sized zip file with several files in it, but the zip file resulting from my code is empty. Here's my code:
// build post request
string targetHref = "http://wwwcf.nlm.nih.gov/umlslicense/kss/login.cfm";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(targetHref);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
// encoding to use
Encoding enc = Encoding.GetEncoding(1252);
// build post string containing authentication information and add to post request
string poststring = "returnUrl=" + fixCharacters(targetDownloadFileUrl);
poststring += getUsernameAndPasswordString();
poststring += "&login2.x=0&login2.y=0";
// convert to required byte array
byte[] postBytes = enc.GetBytes(poststring);
request.ContentLength = postBytes.Length;
// write post to request
Stream postStream = request.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();
// get response as stream
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
// writes stream to zip file
FileStream writeStream = new FileStream(fullZipFileName, FileMode.Create, FileAccess.Write);
ReadWriteStream(responseStream, writeStream);
response.Close();
responseStream.Close();
The code for ReadWriteStream looks like this.
private void ReadWriteStream(Stream readStream, Stream writeStream)
{
// taken verbatum from http://www.developerfusion.com/code/4669/save-a-stream-to-a-file/
int Length = 256;
Byte[] buffer = new Byte[Length];
int bytesRead = readStream.Read(buffer, 0, Length);
// write the required bytes
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, Length);
}
readStream.Close();
writeStream.Close();
}
The building of the post string is taken from my previous forms app that works. I compared the resulting values in poststring for both sets of code (my working forms app and this one) and they're identical.
I'm not even sure how to troubleshoot this further. Anyone see anything obvious as to why this isn't working?
Conclusion 10/23/09
I finally have this working. A couple of important hurdles I had to get over. I had some problems with the ReadWriteStream method code that I got online. I don't know why, but it wasn't working for me. A guy named JB in Claudio Lassala's Virtual Brown Bag meeting helped me to come up with this code which worked much better for my purposes:
private void WriteResponseStreamToFile(Stream responseStreamToRead, string zipFileFullName)
{
// responseStreamToRead will contain a zip file, write it to a file in
// the target location at zipFileFullName
FileStream fileStreamToWrite = new FileStream(zipFileFullName, FileMode.Create);
int readByte = responseStreamToRead.ReadByte();
while (readByte != -1)
{
fileStreamToWrite.WriteByte((byte)readByte);
readByte = responseStreamToRead.ReadByte();
}
fileStreamToWrite.Flush();
fileStreamToWrite.Close();
}
As Will suggested below, I did have trouble with the authentication. The following code is what worked to get around that issue. A few comments inserted addressing key issues I ran into.
string targetHref = "http://wwwcf.nlm.nih.gov/umlslicense/kss/login.cfm";
HttpWebRequest firstRequest = (HttpWebRequest)WebRequest.Create(targetHref);
firstRequest.AllowAutoRedirect = false; // this is critical, without this, NLM redirects and the whole thing breaks
// firstRequest.Proxy = new WebProxy("127.0.0.1", 8888); // not needed for production, but this helped in order to debug the http traffic using Fiddler
firstRequest.Method = "POST";
firstRequest.ContentType = "application/x-www-form-urlencoded";
// build post string containing authentication information and add to post request
StringBuilder poststring = new StringBuilder("returnUrl=" + fixCharacters(targetDownloadFileUrl));
poststring.Append(getUsernameAndPasswordString());
poststring.Append("&login2.x=0&login2.y=0");
// convert to required byte array
byte[] postBytes = Encoding.UTF8.GetBytes(poststring.ToString());
firstRequest.ContentLength = postBytes.Length;
// write post to request
Stream postStream = firstRequest.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length); // Fiddler shows that post and response happen on this line
postStream.Close();
// get response as stream
HttpWebResponse firstResponse = (HttpWebResponse)firstRequest.GetResponse();
// create new request for new location and cookies
HttpWebRequest secondRequest = (HttpWebRequest)WebRequest.Create(firstResponse.GetResponseHeader("location"));
secondRequest.AllowAutoRedirect = false;
secondRequest.Headers.Add(HttpRequestHeader.Cookie, firstResponse.GetResponseHeader("Set-Cookie"));
// get response to second request
HttpWebResponse secondResponse = (HttpWebResponse)secondRequest.GetResponse();
// write stream to zip file
Stream responseStreamToRead = secondResponse.GetResponseStream();
WriteResponseStreamToFile(responseStreamToRead, fullZipFileName);
responseStreamToRead.Close();
sl.logScriptActivity("Downloading update.");
firstResponse.Close();
I want to underscore that setting AllowAutoRedirect to false on the first HttpWebRequest instance was critical to the whole thing working. Fiddler showed two additional requests that occurred when this was not set, and it broke the rest of the script.
You're trying to use UI controls to do something in a windows service. This will never work.
What you need to do is just use the WebRequest and WebResponse classes to download the contents of the webpage.
var request = WebRequest.Create("http://www.google.com");
var response = request.GetResponse();
var stream = response.GetResponseStream();
You can dump the contents of the stream, parse the text looking for updates, and then construct a new request for the URL of the file you want to download. That response stream will then have the file, which you can dump on the filesystem and etc etc.
Before you wonder, GetResponse will block until the response returns, and the stream will block as data is being received, so you don't need to worry about events firing when everything has been downloaded.
You definitely need to re-think your approach (as you've already begun to do) to eliminate the Forms-based application approach. The service you're describing needs to operate with no UI at all.
I'm not familiar with the details of System.WebClient, but since it
provides common methods for sending
data to and receiving data from a
resource identified by a URI,
it will probably be your answer.
At first glance, WebClient.DownloadFile(...) or WebClient.DownloadFileAsync(...) will do what you need.
The only thing I can add is that once you have scraped your screen and have the fully qualified name of the file you want to download, you could pass it along to the Windows/DOS command 'get' which will fetch files via HTTP. You can also script a command-line FTP client if desired. It's been a long time since I tried something like this in Windows, but I think you're almost there. Once you have fetched the correct file, building a batch file to do everything else should be pretty easy. If you are more comfortable with Unix, google "unix services for windows" just keep an eye on the services they start running (DHCP, etc). There are some nice utilities which will let your treat dos as a unix-like shell (ls -l, grep, etc) Finally, you could try another language like Perl or Python but I don't think that's the kind of advice you were looking for. :)