Roku Developer Program

Developers and content creators—a complete solution for growing an audience directly.
cancel
Showing results for 
Search instead for 
Did you mean: 
Romans_I_XVI
Level 8

Is using cacert.pem from the legacy SDK still the appropriate method?

So I have no knowledge of web security and certificates, but I attempted to follow the somewhat vague directions on the roUrlTransfer documentation page.
https://sdkdocs.roku.com/display/sdkdoc/roUrlTransfer

To set up my server I used the instructions found here. - https://matoski.com/article/node-express-generate-ssl/ - Except I swapped in the cacert.pem from the old SDK - https://image.roku.com//www/static/sdk/RokuSDK.zip -

Then I call this for all of my url transfers.
function AddHeaders(URLTransfer as Object)
    URLTransfer.AddHeader("Content-Type", "application/json")
    URLTransfer.AddHeader("X-Roku-Reserved-Dev-Id", "")
    URLTransfer.SetCertificatesFile("common:/certs/ca-bundle.crt")
    URLTransfer.EnablePeerVerification(false)
    URLTransfer.EnableHostVerification(true)
    URLTransfer.InitClientCertificates()
end function


I have to have the peer verification to false or I get this error.
BrightScript Debugger> print msg.getfailurereason()

SSL certificate problem: self signed certificate


I don't know if that matters that it is set to false, or for that matter if host verification needs to be set to true. But it appears that everything is working. If you try to request data from my website outside of the Roku box it declines you with an SSL error, and within my app on the Roku it works fine.
I guess one of the big things I'm asking about is if this system is still the safe and correct system to use, considering I'm using a file from the SDK that is now marked as legacy, and the only instructions I can find about it from within Roku is a forum post by RokuKevin from 2010 - https://forums.roku.com/viewtopic.php?p ... ccb45e1c8a

Any thoughts would be great, and hey if you want to try to break in to my server that'd be great too. Here's an example of a request that the app would make.
https://74.91.115.18:3000/getLevel?level_id=1100

Thanks
0 Kudos
8 Replies
Romans_I_XVI
Level 8

Re: Is using cacert.pem from the legacy SDK still the appropriate method?

Apparently nobody knows...
0 Kudos
EnTerr
Level 9

Re: Is using cacert.pem from the legacy SDK still the appropriate method?

This is the kind of question that needs an answer by a Roku person.
Give it a week or two before worrying - after that, i could join you in condemning the Co. evangelists for hiding in the shadows - but not before.
0 Kudos
Romans_I_XVI
Level 8

Re: Is using cacert.pem from the legacy SDK still the appropriate method?

Lol ok, sounds like a plan Smiley Tongue
0 Kudos
belltown
Level 7

Re: Is using cacert.pem from the legacy SDK still the appropriate method?

"Romans_I_XVI" wrote:
Is using cacert.pem from the legacy SDK still the appropriate method?

I don't see why not. However, you have to use it correctly to get the desired level of security.

"Romans_I_XVI" wrote:
To set up my server I used the instructions found here. - https://matoski.com/article/node-express-generate-ssl/

That's not a very good example. He generates a private key used to sign a Certificate Signing Request for his Certificate Authority, then generates a self-signed certificate for the Certificate Authority -- none of which get used when he generates the self-signed server certificate.

What you need to do depends on what level of authentication you are aiming for:

Server Authentication/Encryption

The authentication part is when you want your Roku channel code to verify that it is communicating with YOUR server, and not some hacker trying to direct traffic from your channel running on his Roku, to his own server, so he can figure out your API. The encryption part prevents anyone from eavesdropping on the connection between your channel running on a user's Roku and your server at any point in the connection.

You can use a self-signed server certificate for this. Your server must be configured to access both the self-signed server certificate and the private key used to sign the server certificate.

The only requirement for your Roku channel to implement Server Authentication is that ifUrlTransfer.SetCertificatesFile() is called to specify a file containing a certificate (chain) that can verify the authenticity of your server certificate, and an "https" url scheme is used. Normally, one would use a server certificate issued by a trusted Certificate Authority (CA), and specify "common:/certs/ca-bundle.crt", which contains a bundle of well-known, publicly-available trusted CA certificates. However, when using a self-signed server certificate, you must specify a file containing the certificate of the self-signed CA used to sign your certificate (if you created your own CA certificate used to sign the server certificate -- or the server certificate itself, if it was purely self-signed, rather than signed by your own CA, as in my example below).

Do not call EnablePeerVerification() or EnableHostVerification() if you're trying to achieve a high level of server authentication. Use the defaults, which are True in both cases. EnablePeerVerification(False) disables the check that the server certificate was signed by a trusted authority, allowing a hacker to present a server certificate pretending to be from your domain, but which could have been signed by the hacker's own CA. EnableHostVerification(False) disables the check that the server certificate was issued to the same domain name used to identify your own server, allowing a hacker to present a certificate signed by a trusted CA, but which was issued for the hacker's domain rather than your own. The only time you might want to call EnablePeerVerification(False) would be if for some reason you're unable to install your server certificate in your Roku channel. In this case you would not have an authenticated connection with the server (and would therefore be subject to 'man in the middle' attacks), although your connection would still be encrypted (if using "https"), preventing eavesdropping.

Client Authentication

This is when you want to ensure that all traffic your server receives comes from an actual Roku device, and not some hacking tool trying to get information from your server.

In this case, the server must be configured for client certificate authentication and have access to the Roku certificate, and the Roku device must be instructed to send its client certificate to the server. The Roku certificate installed in your server is obtained from the Roku SDK file, cacert.pem. All you need do in the Roku channel to ensure the client certificate is sent is call ifUrltransfer.InitClientCertificates().

Roku Developer Authentication

^ That's not a real term -- but I'm referring to verifying that the Roku channel communicating with your server is YOUR Roku channel, and not a Roku channel developed by some hacker.

If you call ifHttpAgent.AddHeader("X-Roku-Reserved-Dev-Id", ""), then any request sent from your Roku channel to your server will contain an HTTP X-Roku-Reserved-Dev-Id header with something that identifies YOU as the developer of the channel. Your server can then read its request headers to extract the developer id. Note that when the channel is side-loaded, it uses a different developer id from that used by a channel installed from the Channel Store. Your server code should take that into account.

Example incorporating all 3 methods

Generate the private key to be used to sign your self-signed server certificate:

openssl genpkey -out server-key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048


Generate the self-signed server certificate:

openssl req -out server.crt -new -key server-key.pem -nodes -x509 -days 3650


Answer the questions used to specify the certificate fields. In particular, ensure that the Common Name (CN) field is the domain name of your server. Make sure you're using an actual domain name rather than an IP address for the server. If you don't have one, you can get a free '.tk' domain from dot.tk.

Put the certificates where your server can access them:

ssl/server-key.pem - server private key
ssl/server.crt - server self-signed certificate
ssl/cacert.pem - Roku SDK certificate

Configure your server, e.g. for node.js:


const https = require('https');
const fs = require('fs');

// Roku application's Developer ID
const rokuDevId = '22c6fabab75e456f25e7e12345e4242c6c1de443';

const options = {
 // Server's private key
 key: fs.readFileSync('ssl/server-key.pem'),

 // Server's self-signed certificate
 cert: fs.readFileSync('ssl/server.crt'),

 // Roku's certificate
 ca: fs.readFileSync('ssl/cacert.pem'),

 // Require Roku to send a client certificate
 requestCert: true,

 // Reject if the client certificate received is not the one in cacert.pem
 rejectUnauthorized: true
};

https.createServer(options, (req, res) => {
 console.log ('user-agent:', req.headers['user-agent']);
 console.log ('x-roku-reserved-dev-id:', req.headers['x-roku-reserved-dev-id']);
 if (req.client.authorized &&
     req.headers['x-roku-reserved-dev-id'] === rokuDevId) {
   res.writeHead(200);
   res.end('Hello, World!\n');
 }
 else {
   res.writeHead(403);
   res.end('Unauthorized\n');
 }

}).listen(443);


Put the self-signed server certificate in your Roku package:

/certs/server.crt

Example Roku code:


Sub Main ()
   port = CreateObject ("roMessagePort")
   ut = CreateObject ("roUrlTransfer")
   ut.SetPort (port)
   ut.SetUrl ("https://myserver.tk/")
   ut.SetCertificatesFile ("pkg:/certs/server.crt")
   ut.InitClientCertificates ()
   ut.AddHeader("X-Roku-Reserved-Dev-Id", "")

   If ut.AsyncGetToString ()
       While True
           msg = Wait (0, port)
           If Type (msg) = "roUrlEvent"
               If msg.GetInt () = 1
                   responseCode = msg.GetResponseCode ()
                   failureReason = msg.GetFailureReason ()
                   If responseCode = 200
                       data = msg.GetString ()
                       Print "Data received: "; data
                   Else
                       Print "Response code: "; responseCode
                       Print "Failure reason: "; failureReason
                   End If
               Else
                   Print "Url transfer did not complete"
               End If
               Exit While
           End If
       End While
   Else
       Print "AsyncGetToString failed"
   End If
End Sub
https://github.com/belltown/
0 Kudos
Romans_I_XVI
Level 8

Re: Is using cacert.pem from the legacy SDK still the appropriate method?

Ok, somebody needs to sticky this thread because that is the most cohesive explanation of this authentication method I could ever imagine. I feel like I really understand it now lol, thank you so much Belltown!
0 Kudos
renojim
Level 10

Re: Is using cacert.pem from the legacy SDK still the appropriate method?

My thanks to belltown as well, although I think I need to read over this a few more times to fully understand it. I've played with self-signed certificates in the past, but I can't say I really knew what I was doing. Smiley Happy

-JT
0 Kudos
EnTerr
Level 9

Re: Is using cacert.pem from the legacy SDK still the appropriate method?

@beltown's above should be invited for publishing as guest post on the RokuCo's Developer Blog!

<grumble> ... instead of surreal pieces on how to combine the worst of both worlds - the absurd complexity of under-baked SDK2 with the venerable (now retro) looks of SDK1.
0 Kudos
RokuChrisT
Level 7

Re: Is using cacert.pem from the legacy SDK still the appropriate method?

"EnTerr" wrote:
@beltown's above should be invited for publishing as guest post on the RokuCo's Developer Blog!

<grumble> ... instead of surreal pieces on how to combine the worst of both worlds - the absurd complexity of under-baked SDK2 with the venerable (now retro) looks of SDK1.

Agreed! @beltown you willing to be a guest blog post on blog.roku.com/developer/?
Ping me if so - thanks for tackling!
0 Kudos