I have an encrypted (128-AES-CTR-NoPadding) video residing on a server which I need to decrypt as it downloads, so that user can stream it (in normal players/web).
I understand the components of this solution and how they should be put together to make this work. It partially works but for the rest I just can't implement streaming right. I have been reading and learning from examples (most of which is playing a file on disk, which is not the case here) on this for the past week and have come to conclusion this is beyond me and I need some help.
Details
I am using a Lightweight webserver (nanoHttpd) acting as a proxy to download the encrypted data from remote server and serve decrypted data. Below are the main codes inside my NanoHTTPD.serve method.
//create urlConnection to encrypted video file with proper headers (ie range headers) as request received by the proxy server
InputStream inputStream = new CipherInputStream(cipher, urlConnection.getInputStream());
return newChunkedResponse(status, contentType,inputStream);
So now if I go to my NanoHttpd webserver (http://localhost:9000), the file starts downloading and after the download completes, the file is fully decrypted and playable as expected. So this ensures that getting encrypted data from the server and serving decrypted data is working correctly. But when any video player (html5, vlc) is asked to stream the video from that url, it simply does not work.
If the above code in NanoHTTPD.serve changed to
//create urlConnection to cleardata video file with proper headers (ie range headers) as request received by the proxy server
InputStream inputStream = urlConnection.getInputStream();
return newChunkedResponse(status, contentType,inputStream);
And then try to stream from the aforementioned players, it'll work just fine. So this ensures that the web proxy is correcting retrieving and feeding data.
Potential problem To support range requests from the video player we will need to correctly skip to block boundary that is a multiple of cipher block size. So it's possible that when video player is requesting data with header (range: bytes 34-44), the CipherInputStream is probably failing to decrypt the data since the inputstream has data from 34-44. But I am at a loss on how to do this with urlConnection.getInputStream() and CipherInputStream.
But even without this, it should at least start playing the first few seconds because the first request video player sends is (range: 0-) which means inputStream is starting from index 0 so CipherInputStream should be able to decrypt and serve those initial bytes and the video should continue playing.
I am at a complete loss because I don't know how to debug this. Any ideas, sample codes are welcome, I'll try them out and post the results here.
I have figure this out. I'll post the solution here for others.
The problem here was the ranged requests. If the proxy does not send proper responses to these range requests, the playback will fail. This can fail due to a number of reasons.
As far as code samples go, I only had to add one line before
InputStream inputStream = new CipherInputStream(cipher, urlConnection.getInputStream());
return newChunkedResponse(status, contentType,inputStream);
which is
jumpToOffset(cipher,....);
After this everything was working correctly including seeking of the video.