Monday, December 01, 2008

JW Player, Security and EdgeCast

Hello kinder,

I'm currently doing some work with a company that wants to stream their media. Their site does not have that kind of bandwidth nor do they have the storage space so the next best solution was to outsource. They did trials with a few companies and www.edgecast.com won out. One of the criteria for choosing a streaming media company is security of files so that any 'Joe sixpack' or 'hockey mom' couldn't save the urls and send them to their friends therefore eating up your bandwidth and money.

So what does EdgeCast do for their security? They use token authentication. Essentially you can preshare a 'token' and then encrypt that along with other data to create an encrypted string that will be used to authorize the viewer. As 'integrationman' my job was to integrate ColdFusion and their token authentication. Surely this would be simple, well I was both wrong and right.

Process 1 - the official way + ColdFusion = failboat
The official way is to use their Blowfish binaries and exe to encrypt the string. Sure this would be simple enough with cfexecute if we weren't on a shared hosing provider (I feel the failboat waning). To add insult to injury I tried just using the built in ColdFusion <cfencrypt> tag with the blowfish algorythm and SALT etc, but that ultimately produced a string that would not match their encrypted string and therefore capsized my failboat. Finally after many hours of failing, one of their wizards said, "hey why don't they just use the SOAP api we have." Well DUH to me (I really wish they told me they had this many many hours earlier :sigh: )

Process 2 - the better way (aka SOAP)
So finally after they sent me the info on the SOAP calls this was a breeze to set up. The short part of this is that you can either use standard CF SOAP calls or go the easy route and just use CFHTTP since we only need to encrypt our token.
  1. Send CFHTTP request to:
    https://api.edgecast.com/v1/mediamanager.asmx/TokenEncrypt
  2. Pass 3 Parameters:
    • strCredential = string in the format:
      c:bas:YOURUSERNAME:YOURPASSWORD
      "c:bas:" is required and is not modified. Replace the username and password with your username and password specified in the "ADMINISTRATION -> USERS" section of the Control Panel. Make sure you do not give this user any privileges like file uploads since you will be putting the username/password either in a db or in the code.
    • strKey = preshared token
    • strArgs = any extra parameters you need to pass (minimum is "ec_expire=#####")
  3. That CFHTTP call will return an XML string that you will parse for the encrypted string and use in your JW player.
Example Code:

<!--- MAKE THE EXPIRE TIME 30 SECONDS FROM NOW --->
<CFSET VARIABLES.tmpDate = DateAdd("s", 30, Now()) />
<!--- CONVERT TO EPOCH TIME --->
<CFSET VARIABLES.expDate = DateDiff("s", "January 1 1970 00:00", VARIABLES.tmpDate) />


<!--- MAKE CFHTTP REQUEST TO GET ENCRYPTED TOKEN --->
<CFHTTP method="POST" URL="https://api.edgecast.com/v1/mediamanager.asmx/TokenEncrypt">
<CFHTTPPARAM NAME="strCredential" VALUE="c:bas:USERNAME:PASSWORD" TYPE="formfield" />
<CFHTTPPARAM NAME="strKey" VALUE="YOURSHAREDTOKEN" TYPE="formfield" />
<CFHTTPPARAM NAME="strArgs" VALUE="ec_expire=#variables.expDate#" TYPE="formfield" />
</CFHTTP>


<CFIF FindNoCase("200", CFHTTP.StatusCode)>
<!--- PARSE XML --->
<CFSET VARIABLES.xmlContent = xmlparse(CFHTTP.FileContent) />
<CFSET VARIABLES.encText = xmlContent.string.xmlText />
<!--- CREATE VIEWER --->
<script type="text/javascript" src="swfobject.js"></script>
<div id="container"><a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Player</a> to see this player.</div>
<script type="text/javascript">
var s1 = new SWFObject("player.swf","ply","640","480","9","#FFFFFF");
s1.addParam("allowfullscreen","true");
s1.addParam("allowscriptaccess","always");
s1.addVariable('autostart', 'true');
s1.addVariable('type', 'video');
s1.addVariable('streamer', 'rtmp://flash.edgecastcdn.net/#####/YOURAUTHFOLDER/');
s1.addVariable("file",'mp4:YOURFILE.mp4?<CFOUTPUT>#VARIABLES.encText#</CFOUTPUT>');
s1.write("container");
</script>
</CFIF>


Notes:
  • When setting the 'streamer' variable in the JW Player make sure you use your path that is in the FLASH->FLASH STREAMING section of the admin. They automatically list "/videos/example" as the folder, however you need to choose a folder yourself that will be used for the authenticated videos. To do this, go into the FLASH->TOKEN AUTH section and add a PATH like "/protectedfiles" (make sure you create the folder first via ftp or the MEDIA MANAGER). Now wait a half hour or so like it says, then you can use this.
  • When setting the 'file' variable in the JW Player make sure you set it in the format: TYPE:FILENAME.EXTENSION?ENCRYPTEDSTRING
    i.e.: 'mp4:YOURFILE.mp4?asdfkl234234jsaf...
Hope this helps some other developers out there to get their job done quicker!

Happy coding!

Sunday, March 30, 2008

Things I Hate About JAVA

It's ranting time and I am officially enclosing this whole blog in a big fat rant tag:

<rant>

First off I'm not an über java programmer so coming from other languages I've picked up a thing or two about how I would expect languages to be programmed based on convenience and consistency. I'll keep this short and only hit my major gripes:

Why Java is EVIL:
  1. Bad plugin implementation - how many times has an applet crashed your browser or not work cross platform? Granted it has come a long way and it really depends on who programmed the applet, but in general you have to blame SUN for poor implementation. They should have fixed it way back in 1.0. If they did flash most likely would never have hit the market. Now I find client perception in Java to be slower than Flash (especially AS3).
  2. Programmatic Inconsistencies - why is it that to get a length of a string you use: string.length() and for an array: array.length; and for a vector: vector.size(). Who programmed this crappy language? Yes I understand that a string is an object and that length is a property of that object, but why not make a function? I understand the concepts however backwards they are.

    I have an idea: how about using a method for everything and calling it .length() ? Just make all arrays have a default method called length(). Same with vectors (why oh why did they choose size() ). Remember if you want people to pick up your language, make it easy for them to do so.
  3. Java Docs - OHHHH how evil these are. They are god awful in design and usage. Their initial examples suck (if they even have any). Why not have an example for everything in there (method specifically). If you have a method called length() give me an example. I don't care how simple, just an example. Don't make me search the web to find an example when you could easily make one. Remember if you want people to pick up your language, make it easy for them to do so.
  4. Last but not least I HATE - I repeat HATE "Null pointer exception" . I give this one big WTF Sun. Yes I get the premise behind it - I am using a variable that hasn't been initialized or something else along those lines. Don't just tell me "haha sucks for you, you have a null pointer exception" TELL me where it is. Where am I calling it, what is the name of the variable/object etc that is null? Lets take a look at a JSP example (since I happen to be working on this now):
    [31/Mar/2008:08:29:15] failure (15460): Internal error: servlet service function had thrown ServletException (uri=/cp/www/registration.jsp):$
    at org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:453)
    at _jsps._www._registration_jsp._jspService(_registration_jsp.java:826)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:119)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
    at org.apache.jasper.servlet.JspServlet$JspServletWrapper.service(JspServlet.java:259)
    at org.apache.jasper.servlet.JspServlet$JspServletWrapper.access$6(JspServlet.java:249)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:530)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:599)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
    at com.iplanet.server.http.servlet.NSServletRunner.invokeServletService(NSServletRunner.java:943)
    at com.iplanet.server.http.servlet.WebApplication.service(WebApplication.java:1094)
    at com.iplanet.server.http.servlet.NSServletRunner.ServiceWebApp(NSServletRunner.java:1005)
    , root cause: java.lang.NullPointerException
    at _jsps._www._registration_jsp._jspService(_registration_jsp.java:620)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:119)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
    at org.apache.jasper.servlet.JspServlet$JspServletWrapper.service(JspServlet.java:259)
    at org.apache.jasper.servlet.JspServlet$JspServletWrapper.access$6(JspServlet.java:249)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:530)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:599)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
    at com.iplanet.server.http.servlet.NSServletRunner.invokeServletService(NSServletRunner.java:943)
    at com.iplanet.server.http.servlet.WebApplication.service(WebApplication.java:1094)
    at com.iplanet.server.http.servlet.NSServletRunner.ServiceWebApp(NSServletRunner.java:1005)


    Where is the line number in the JSP? What variable? What object? Throw me a bone here Sun! Now I have to use System.out.println to track down the freaking line that it is on. Yes I know you can go to the java file that is precompiled and try to find it in there. That sometimes works, however System.out.println 's are often just as fast.

    ALSO - I give kudos to Macrobe for making ColdFusion user friendly. ColdFusion is built on top of a Java runtime and when an error is throwing in CF it (usually) tells you the line number/variable etc etc. Good Job guys! I imagine your programmers went through hell to get that working.
Java how I hate thee...

</rant>

Friday, March 14, 2008

IE 7 + Flash 8 ExternalInterface = BUGTASTIC!

<rant>GRRRRR BROWSER INCOMPATIBILITIES!!!!</rant>

For the past few nights I have been working on and off with a very simple (or what should have been simple) Flash => JavaScript => Flash application. The plan was to use swfobject to embed a flash movie within a form and make the submit button flash. Then after validating the form when the flash button was clicked a sound would play (don't ask - client requested said ability).

So in order to accomplish something like this I created an html form; put the flash div within the form (along with the js call to render); and then used ExternalInstance to trigger the function call on(release) within flash. FireFox worked like the trooper it is. Then comes that dreaded IE 7 test. All aboard the failboat! Every time I get the same stupid error on Line 1: "savebutton" is not defined (savebutton was what I named my flash movie). WTF. I tried everything and traced every variable/function call but nothing. After much Google digging I came upon this article by Steve Kamerman:

He said that IE has a nifty little bug where it can not reference a flash movie properly within a form tag. This was both extremely disheartening as well as a great joy. My flash button was the submit button so I just moved it as well as the flash div that I call in swfobject after the closing form tag and it worked! Thanks Steve for the hint!

Success!

Wednesday, January 02, 2008

Cryptography samples for dummies (RSA/DSA)

Over this past year I had the pleasure (read - angst, woe, terror) of trying to figure out how to use java cryptography. At the time we were running Luminis 3.3 (we still are, but hopefully not for much longer) and we were in the process of implementing GMail for our school which would tie into Luminis authentication. In short (I'll save the gory details for another time) we decided to use SAML (Security Assertion Markup Language) to tell Google that the user is properly authenticated to Luminis and we say that he/she is OK in our book so let them read their e-mail on your servers. This is all well and good, however during the setup 2 major things came up relating to cryptography.

Problem 1: Generating DSA keys for Google


In order for this whole SAML business to work we need to send an encrypted packet with information relating to what user is properly authenticated to Google. Google being the sly folks they are, said that they don't want this information in plain text so 'you utes need to encrypt it with DSA. Now send us the keys to the palace so we can decrypt it after you send it encrypted.' Hence the first problem, how do I generate DSA keypairs? I did not see this on Google's code site so I scoured the interweb for a few nibblets of info and I came across a sample which I updated ever so slightly (see attached file genDSAKeys.java at the end of this post). I apologize for not having the links to give credit as this was about a year ago now when I was searching. I had to dig this up now for other stuff I'm doing. Well after compiling this file with JDK 1.5 (at the time) and now with 1.6, it generates the public and private keys and saves it out to 2 files.
javac genDSAKeys.java
java genDSAKeys

I ran this on a Windows XP box fyi and then sent the goods to Google and all is swell.

Problem 2: WTF Luminis 3.3 only has JRE1.3 on it and GMail APIs require 1.4+??? Cryptography stuff only started coming out with neat packages in java 1.4!!!


More background since this is relevant. We are running Luminis 3.3 on Solaris and the only JRE they (Sungard/uPortal) supports is 1.3. So now comes the fun part, when using the Google Provisioning API to set up GMail authentication via SAML, it required a few servlets and JSPs in order to generate the tokens and do encryption and their API was written specifically for JRE 1.4+. I first tried compiling it on 1.3, however it failed miserably and I didn't think rewriting Google's api would help upgrades.

What we ended up doing is setting up a second box just for Tomcat 5.5 running JRE 1.5. This worked beautifully with Google's api code. Now comes the uber problem, interserver communications sent via the browser are not secure (bleh!). So we though the usual stuff like SSL, but it really comes down to this. We want the packets secure and we want the content within the packet protected to the fullest as it is being passed back and forth. As a result we looked into encryption.

Since we had a mixed environment of 1.3 and 1.5 JREs we had problems. After bouncing for a few hours in one of those bouncy castles at a carnival (and also endless hours of searching for anything cryptographically related), I came across an awesome site:


This site provides cryptography libraries for Java version 1.1 and up! It's a beautiful thing (and FREE). So now comes the fun part, we downloaded the jars for 1.3 and 1.5 and wrote some code to do encryption/decryption of the content sent back and forth between the servers. However back to the topic of this post - as with most encryption that has decryption you need a public/private key pair. So I took a look around and the DSA stuff I found didn't have an example (and as you can guess I'm not an evil genius when it comes to cryptography). So I bit the bull by the nose on a grindstone and pieced together numerous examples to create one single example that would generate RSA public and private keys. I chose RSA since that was an easy option that bouncy castle supported (they support a ton of stuff - check them out and did I mention it was FREE).

After a while I created the final java file that creates public/private keys, creates a string, encrypts it, decrypts it and writes out the keys to files. It is everything I ever wanted in an example and now it can be yours for the low low price of not flaming me on how easy this would be if you did A, B, or C ;-)
javac kGenerateKeys.java
java kGenerateKeys

Download source files below:
genDSAKeys.java (DSA Example)
kGenerateRSAKeys.java (RSA Example)

The code has some comments in there for the good stuff so feel free to take a look.