On a linux box, I have an MP4 video that is encrypted with openssl:
openssl enc -aes-128-ecb -a -in video.mp4 -out video.enc -K `cat aes.key`
Please note, this is an exercise, the strength of the algo doesn't matter.
That file is sent to an Android app, and I'm trying to play it using ExoPlayer.
I've done some tests beforehand with text files to make sure the decryption was working properly
fun decrypt(key: ByteArray, data: ByteArray): ByteArray {
val spec = SecretKeySpec(key, "AES")
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, spec)
globalCipher.init(Cipher.DECRYPT_MODE, spec)
return cipher.doFinal(data)
}
Regarding ExoPlayer, it's a bit overwhelming between AesCipherDataSource
, AesCipherDataSink
, SimpleCache
etc. I was not able to put together a simple way to play the video.
fun playVideo() {
val player = SimpleExoPlayer.Builder(this).build()
playerView.player = player
val dataSourceFactory = DefaultDataSourceFactory? // <-- what's the factory?
val dataSource = AesCipherDataSource(globalCipher, ?) // <-- what's the data source?
val extractorsFactory: ExtractorsFactory = DefaultExtractorsFactory()
try {
val uri = Uri.fromFile(File(path, "video.enc"))
val videoSource =
ExtractorMediaSource(uri, dataSourceFactory, extractorsFactory, null, null)
player.prepare(videoSource)
player.playWhenReady = true
} catch (e: Exception) {
e.printStackTrace()
}
}
So questions:
here's the solution. Might need some adjustments to handle skipping frames, fast forward etc, but this plays an AES/ECB/PKCS5Padding
encrypted video
class EncryptedDataSourceFactory(
private val key: String
) : DataSource.Factory {
override fun createDataSource(): EncryptedDataSource =
EncryptedDataSource(key)
}
class EncryptedDataSource(private val key: String) : DataSource {
private var inputStream: CipherInputStream? = null
private lateinit var uri: Uri
override fun addTransferListener(transferListener: TransferListener) {}
override fun open(dataSpec: DataSpec): Long {
uri = dataSpec.uri
try {
val file = File(uri.path)
val skeySpec = SecretKeySpec(key.toByteArray(), KeyProperties.KEY_ALGORITHM_AES)
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, skeySpec)
inputStream = CipherInputStream(file.inputStream(), cipher)
} catch (e: Exception) {
}
return dataSpec.length
}
@Throws(IOException::class)
override fun read(buffer: ByteArray, offset: Int, readLength: Int): Int =
if (readLength == 0) {
0
} else {
inputStream?.read(buffer, offset, readLength) ?: 0
}
override fun getUri(): Uri? =
uri
@Throws(IOException::class)
override fun close() {
inputStream?.close()
}
}
private fun playVideo(key: String) {
val player = SimpleExoPlayer.Builder(this).build()
playerView.player = player
val dataSourceFactory: DataSource.Factory = EncryptedDataSourceFactory(key)
val extractorsFactory: ExtractorsFactory = DefaultExtractorsFactory()
try {
val uri = Uri.fromFile(video)
val videoSource: MediaSource = ExtractorMediaSource(uri, dataSourceFactory, extractorsFactory, null, null)
player.prepare(videoSource)
player.playWhenReady = true
} catch (e: Exception) {
e.printStackTrace()
}
}