CloudFront Secure streaming

We’ve had a bit of an issue figuring out how to use CloudFront for a protected stream. We have preview episodes which are okay to use public, not https url’s, but for all full catalog, we need to have security in place.

Can someone who uses Amazon CloudFront point us in the right direction as to what exactly we need to do to get our Roku channel to authenticate with our private CloudFront URLs? Where in the code do we deal with SSL certificates?

I found somewhat of a solution for this with expiring URLs. I’d like a better solution.

It appears if you have a ? in the URL, the Roku will not read the URL correctly. So after I generate the expiring URL with authentication keys, I’m using a URL shortener hosted on my server to create a new URL. Then I’m having the XML file written dynamically and updated through a cron job.

Hope this helps someone. Or if somebody has a better solution, let me know.

I don’t believe there is any reason why a ? in a url would cause any problems whatsoever. What might cause problems is a failure to urlencode the parameters in the URL after the ?.

  • Joel

Joel -

Can you provide an example of how that would be structured?

Just to be clear, the question mark in your URL is the marker that begins the URL parameters, correct? Not some other question mark embedded in the parameters or anything like that? Can you provide an example of the kind of URL you are using?

–Mark

“ShifterFilms” wrote:
Joel -

Can you provide an example of how that would be structured?

you might create your url like this:

url="http://myserver.com/restAPI?getPopSongs="+urlencode("Justin Bieber")+"&grammyNominated=true"

with a urlencode function like this:

function urlencode(text as string) as string
xfer=createobject("roUrlTransfer")
return xfer.urlencode(text)
end function

The URL would be something like this:

http://d34TCSSDs9r8qxa.cloudfront.net/VIDEONAME.mp4?Expires=4340056027&Signature=xxce456iLCc5phPnt8NrH~orflXZs34rljsgfnkjx5sntPXj7Ov~YQ50ZOHsdafeTTfawoiLG1ndNxB5C3uh8Zsv2qil6Z2QGradEA34M9y8y-kNZ2lSbiwaVOEYt0UYh3TIvpKIpS9Mz15TSrsksioDSS~rC-U_&Key-Pair-Id=A45RRTQASD456LDF

Since there are tildes in the parameters, they need to be encoded as RokuJoel said. There are three parameters (Expires, Signature, and Key-Pair-Id), so each of the 3 values should be URL encoded.

–Mark

“RokuMarkn” wrote:
Since there are tildes in the parameters, they need to be encoded as RokuJoel said. There are three parameters (Expires, Signature, and Key-Pair-Id), so each of the 3 values should be URL encoded.

–Mark
Yes, tildes (“~”) should be encoded, according to RFC 1738. However, it appears that UrlEncode () does not encode tildes.

I ran the following code snippet:


ut = CreateObject ("roUrlTransfer")
For c = 0 To 127
    ch = Chr (c)
    urlEncode = ut.UrlEncode (ch)
    If Len (urlEncode) = 1 And ch = urlEncode
        Print "Did not encode: " + ch
    EndIf
End For

and got the following result:


------ Running ------
Did not encode: -
Did not encode: .
Did not encode: 0
Did not encode: 1
Did not encode: 2
Did not encode: 3
Did not encode: 4
Did not encode: 5
Did not encode: 6
Did not encode: 7
Did not encode: 8
Did not encode: 9
Did not encode: A
Did not encode: B
Did not encode: C
Did not encode: D
Did not encode: E
Did not encode: F
Did not encode: G
Did not encode: H
Did not encode: I
Did not encode: J
Did not encode: K
Did not encode: L
Did not encode: M
Did not encode: N
Did not encode: O
Did not encode: P
Did not encode: Q
Did not encode: R
Did not encode: S
Did not encode: T
Did not encode: U
Did not encode: V
Did not encode: W
Did not encode: X
Did not encode: Y
Did not encode: Z
Did not encode: _
Did not encode: a
Did not encode: b
Did not encode: c
Did not encode: d
Did not encode: e
Did not encode: f
Did not encode: g
Did not encode: h
Did not encode: i
Did not encode: j
Did not encode: k
Did not encode: l
Did not encode: m
Did not encode: n
Did not encode: o
Did not encode: p
Did not encode: q
Did not encode: r
Did not encode: s
Did not encode: t
Did not encode: u
Did not encode: v
Did not encode: w
Did not encode: x
Did not encode: y
Did not encode: z
Did not encode: ~

So it looks like UrlEncode encodes all characters except: hyphen (“-”), underscore (“_”), period (“.”), alphanumerics and tilde (“~”).

I don’t believe this is the correct behavior according to RFC 1738.

Also, when I run the above code using Escape () instead of UrlEncode () I get the exact same output even though they are supposed to encode differently.

Is this a bug, or am I missing something here?

This is the partial solution to creating the CloudFront URLs needed. This PHP function will create valid expiring URL’s CloudFront likes:


function getSignedURL($resource, $timeout)
{
	//This comes from key pair you generated for cloudfront
	$keyPairId = "YOURKEYPAIRIDHERE1";

	$expires = time() + $timeout; //Time out in seconds
	$json = '{"Statement":[{"Resource":"'.$resource.'","Condition":{"DateLessThan":{"AWS:EpochTime":'.$expires.'}}}]}';		
	
	//Read Cloudfront Private Key Pair
	$fp=fopen("your-keyfile.pem","r"); 
	$priv_key=fread($fp,8192); 
	fclose($fp); 

	//Create the private key
	$key = openssl_get_privatekey($priv_key);
	if(!$key)
	{
		echo "<p>Failed to load private key!</p>";
		return;
	}
	
	//Sign the policy with the private key
	if(!openssl_sign($json, $signed_policy, $key, OPENSSL_ALGO_SHA1))
	{
		echo '<p>Failed to sign policy: '.openssl_error_string().'</p>';
		return;
	}
	
	//Create url safe signed policy
	$base64_signed_policy = base64_encode($signed_policy);
	$signature = str_replace(array('+','=','/'), array('-','_','~'), $base64_signed_policy);

	//Construct the URL
	$url = $resource.'?Expires='.$expires.'&Signature='.$signature.'&Key-Pair-Id='.$keyPairId;
	
	return $url;
}

My solution was to take this function and use it in conjunction with a URL shorterning script. That way I didn’t have to deal with any BrightScript to format the URL’s properly. The other solutions presented are probably cleaner, but if you’re a little BrightScript retarded like myself, this could be a solution for you.

belltown,
Yes, the incorrect handling of tilde is a bug. I have filed a bug report.

–Mark

Hi Mark -

So with tildes is there any other option besides what I’m doing using a URL shortener?

Not a big deal, my solution seems to be working. I’ve set up a cron to run the script hourly, and my URL’s expire after two hours. Now that the shortener has been scripted in, I don’t see the point in undoing it.

Is there any reason I would not want to use a URL shortener?

Actually it has been pointed out to me that RFC 3986 supersedes 1738, and it changes tilde to an unreserved character. So I think the current behavior is correct, and the tildes in the URL don’t need to be encoded.

–Mark

“RokuMarkn” wrote:
Actually it has been pointed out to me that RFC 3986 supersedes 1738, and it changes tilde to an unreserved character. So I think the current behavior is correct, and the tildes in the URL don’t need to be encoded.

–Mark
Yes, it does look like UrlEncode () and Escape () both encode according to RFC 3986, in which case the bug would apply to the SDK documentation. The documentation states that Escape () will “URL encode the specified string using CURL” and that UrlEncode () uses an “Alternate URL encoding scheme with stricter RFC 1738 compliance”. It appears that they both do the same thing, which is to encode according to RFC 3986.

I don’t think any of this helps ShifterFilms with his problem, since none of the characters in the url he gave would require encoding, according to RFC 3986, although I agree that it’s a good idea in general to encode parameters in the url query string.

Thanks for all of the help and suggestions!

With the URL encode, unless there’s a good reason to not do it, I’m sticking with the set-up of using a URL shortener. I installed YOURLS open source software to shorten URLs.

My PHP script creating XML files sends the URL created from the script I posted earlier and creates a shortened URL without any parameters added to the URL.

Then a cron job on the server runs hourly to create new URLs. Thus I have expiring URLs that CloudFront likes and the entire process is automated.

It seems to be working perfectly for my needs.