Android Question Large Video File Encryption

Charlie Burnham

Member
Licensed User
Hello,

I am using Cam2Exe to record MP4 video files up to 330MB. I need to encrypt these files using AES 264 and then zip some of them together onto an external SD Card for later upload. I am getting OOM error when I do:

BytesToEncrypt = Bit.InputStreamToBytes(File.OpenInput(SessionDir,MP4_FileName))
EncryptedBytes = Cipher.Encrypt(BytesToEncrypt,kg.Key,True) 'encrypt bytes

and the mp4 is > 90MB or so. I have tried SetApplicationAttribute(android:largeHeap, "true", but does not help. The amount of time it takes to encrypt does not matter, so I think I need a way of doing the encryption in chunks to temp files. Problem is I do not know how to merge these files back into a single encrypted file. Some sort of file encryption that does this buffering automatically would be good, ad perhaps "Alice" does this. However I need an example of this usage.
 

Charlie Burnham

Member
Licensed User
Thank you! I adapted the above solution to B4A (by removing JFX and MainForm lines) This will encrypt a 240MB file in about 4 seconds on my Moto5. I am having trouble decrypting in .net, however, using Chris Lee's BXEncryption.dll. Using same password in all cases, I can encrypt/decrypt on both Android and Windows sides (within each app), but not across platforms. (I only need to go from B4A to Windows, not reversed). On small files (~50 bytes), I get:

Org.BouncyCastle.Crypto.DataLengthException
HResult=0x80131500
Message=last block incomplete in decryption
Source=BouncyCastle.Crypto

On a 240MB file, when decrypting on Windows side, I get:

1587143908101.png


It looks like the RAF chunking is introducing something the BXEncryption.dll cannot handle. Or, possibly, I need to do something more because these are (mostly) binary files and there is b64 encoding happening somewhere? Or because JFX library was used in original B4X solution. It would be easiest to change the C# source for BXEncryption.dll, I think, but I thought I would post here before trying Chris directly. Un-decryptable file and plaintext files attached (password = "Password").
 

Attachments

  • MCCB_D-840999-000-12_VST_BL_ADM_XXX_17_APR_2020_09-05-35_EN_NOTE_L31_.ENC.txt
    60 bytes · Views: 129
  • MCCB_D-840999-000-12_VST_BL_ADM_XXX_17_APR_2020_09-05-35_EN_NOTE_L31_.txt
    31 bytes · Views: 121
Upvote 0

Charlie Burnham

Member
Licensed User
Yes, file encrypted on Android device decrypts fine on Windows 10 using BVJ jar. BVJ is brilliant, BTW. This led me to examine my .net port a bit more thoroughly. It turned out I was reading in the 4 "blocklength" bytes as .net Int32, which is reversed endian from the way Android writes this value into the stream on the encrypting side. Just needed to reverse the byte order in .net before bitconverting to Int32. This was the only asymmetry in the entire process (not anything at all to do with the bouncy castle libraries). Happy to share this code if anyone is interested. Thanks again.
 
Upvote 0

omo

Active Member
Licensed User
Longtime User
Yes, file encrypted on Android device decrypts fine on Windows 10 using BVJ jar. BVJ is brilliant, BTW. This led me to examine my .net port a bit more thoroughly. It turned out I was reading in the 4 "blocklength" bytes as .net Int32, which is reversed endian from the way Android writes this value into the stream on the encrypting side. Just needed to reverse the byte order in .net before bitconverting to Int32. This was the only asymmetry in the entire process (not anything at all to do with the bouncy castle libraries). Happy to share this code if anyone is interested. Thanks again.
Please, kindly share the code, I am interested
 
Upvote 0

Charlie Burnham

Member
Licensed User
I wrote this in VB.net, but should be simple conversion to C#. To encrypt, you would byte-reverse the Int32 before writing bb.length into the encryped stream

B4X:
Imports System.IO
Imports System.Text
Imports System.Threading
Imports B4XEncryption


Dim Password As String = "Password"


Private Sub Chunk_Decrypt_File(SourcePath As String, DestPath As String)

        Dim fs As New FileStream(SourcePath, FileMode.Open, FileAccess.Read)
        Dim size As Long = fs.Length
        Dim rafInput As New BinaryReader(fs)
        Dim fso As New FileStream(DestPath, FileMode.OpenOrCreate, FileAccess.ReadWrite)
        Dim rafOutput As New BinaryWriter(fso)
        Dim b() As Byte = New Byte(0) {}
                
        Dim enc As B4XCipher
    
        Do While rafInput.BaseStream.Position < size
            Dim RawBlockSizeBytes() As Byte = rafInput.ReadBytes(4)          'Added step
            Array.Reverse(RawBlockSizeBytes)                                               'Added Step
            Dim blockLength As Integer = BitConverter.ToInt32(RawBlockSizeBytes, 0)   'Now results in correct blocklength
            If b.Length <> blockLength Then
                ReDim b(blockLength - 1)
            End If
            b = rafInput.ReadBytes(b.Length)
            Dim bb() As Byte = enc.Decrypt(b, Password)
            rafOutput.Write(bb)
            ProgressBar1.Value = rafInput.BaseStream.Position / size * 100
            Thread.Sleep(30)
        Loop
        rafInput.Close()
        rafOutput.Close()

        MsgBox("Done decryption")
    End Sub
 
Upvote 0

omo

Active Member
Licensed User
Longtime User
I wrote this in VB.net, but should be simple conversion to C#. To encrypt, you would byte-reverse the Int32 before writing bb.length into the encryped stream

B4X:
Imports System.IO
Imports System.Text
Imports System.Threading
Imports B4XEncryption


Dim Password As String = "Password"


Private Sub Chunk_Decrypt_File(SourcePath As String, DestPath As String)

        Dim fs As New FileStream(SourcePath, FileMode.Open, FileAccess.Read)
        Dim size As Long = fs.Length
        Dim rafInput As New BinaryReader(fs)
        Dim fso As New FileStream(DestPath, FileMode.OpenOrCreate, FileAccess.ReadWrite)
        Dim rafOutput As New BinaryWriter(fso)
        Dim b() As Byte = New Byte(0) {}
               
        Dim enc As B4XCipher
   
        Do While rafInput.BaseStream.Position < size
            Dim RawBlockSizeBytes() As Byte = rafInput.ReadBytes(4)          'Added step
            Array.Reverse(RawBlockSizeBytes)                                               'Added Step
            Dim blockLength As Integer = BitConverter.ToInt32(RawBlockSizeBytes, 0)   'Now results in correct blocklength
            If b.Length <> blockLength Then
                ReDim b(blockLength - 1)
            End If
            b = rafInput.ReadBytes(b.Length)
            Dim bb() As Byte = enc.Decrypt(b, Password)
            rafOutput.Write(bb)
            ProgressBar1.Value = rafInput.BaseStream.Position / size * 100
            Thread.Sleep(30)
        Loop
        rafInput.Close()
        rafOutput.Close()

        MsgBox("Done decryption")
    End Sub
Thanks for your readyiness to help. I thought you implemented it in B4a and B4j, that is exactly what I need if you did.
Regards
 
Upvote 0

omo

Active Member
Licensed User
Longtime User
Upvote 0

Charlie Burnham

Member
Licensed User
This is the working test app I made in B4A: (The plain and encrypted filenames are hard-coded). It may or may not be important to use bcprov-jdk15on-150 rather than the latest 165 one. All of the UI stuff can be removed (except progress bar).

B4X:
#Region Project Attributes
#ApplicationLabel: B4A Encrypt
#VersionCode: 1
#VersionName:
'SupportedOrientations possible values: unspecified, landscape or portrait.
#SupportedOrientations: unspecified
#CanInstallToExternalStorage: False

#End Region
#Region  Activity Attributes
#FullScreen: False
#IncludeTitle: True
#End Region
#AdditionalJar: bcprov-jdk15on-150
Sub Process_Globals
'These global variables will be declared once when the application starts.
'These variables can be accessed from all modules.
'Private fx As JFX
'Private MainForm As Activity
End Sub
Sub Globals
'These global variables will be redeclared each time the activity is created.
'These variables can only be accessed from this module.
Dim RootName As String
Dim InputFile As String
Dim OutputFile As String
Private ProgressBar1 As ProgressBar
Private Label1 As Label
Private Label2 As Label
Private Label3 As Label
Private Label4 As Label
Private Label8 As Label
Private Label10 As Label
End Sub
Sub Activity_Create(FirstTime As Boolean)
'Do not forget to load the layout file created with the visual designer. For example:
Activity.LoadLayout("1")
Activity.LoadLayout("1") 'Load the layout file.
'MainForm.Show
Dim rp As RuntimePermissions
 RootName = rp.GetSafeDirDefaultExternal("")
 InputFile = RootName & "/MCCB_D-840999-000-12_VST_BL_ADM_XXX_17_APR_2020_09-06-02_EN_PT_01_OF_$$_XXXXXXXXX_V4800_.mp4"
OutputFile = RootName & "/MCCB_D-840999-000-12_VST_BL_ADM_XXX_17_APR_2020_09-06-02_EN_PT_01_OF_$$_XXXXXXXXX_V4800_.ENC"
EncryptFile(InputFile,OutputFile,"Password")
Sleep(200)
DecryptFile(OutputFile,"Password")
End Sub
Sub Activity_Resume
End Sub
Sub Activity_Pause (UserClosed As Boolean)
End Sub
Sub EncryptFile (Input_Path As String, Output_Path As String, Password As String)
'full paths with filenames
Dim size As Long = File.Size(Input_Path, "")
Dim rafInput, rafOutput As RandomAccessFile
rafInput.Initialize(Input_Path, "", True)
rafOutput.Initialize(Output_Path, "", False)
Dim enc As B4XCipher
Dim buffer(2024 * 1024) As Byte
'Buff size = 2072576 &H1FA000‬

Do While rafInput.CurrentPosition < size
Dim count As Int = rafInput.ReadBytes(buffer, 0, Min(buffer.Length, size - rafInput.CurrentPosition), rafInput.CurrentPosition)
Dim b() As Byte
If count = buffer.Length Then
b = buffer
Else
Dim b(count) As Byte
Bit.ArrayCopy(buffer, 0, b, 0, count)
If Label2.Text.Length < 50 Then
For x = 0 To b.Length -1 'represent text file content
'Label2.Text = Label2.Text & b(x) & ","
Next
End If
End If
Dim bb() As Byte = enc.Encrypt(b, Password)
If Label3.Text.Length < 50 Then
For x = 0 To bb.Length -1 'represent text file content
'Label3.Text = Label3.Text & bb(x) & ","
Next
End If
Label4.Text ="ENC byte count: " & bb.Length
rafOutput.WriteInt(bb.Length, rafOutput.CurrentPosition)

rafOutput.WriteBytes(bb, 0, bb.Length, rafOutput.CurrentPosition)
ProgressBar1.Progress = (rafInput.CurrentPosition / size) * 100
Label1.Text = rafInput.CurrentPosition & "/" & size
Sleep(30)
Loop
rafInput.Close
rafOutput.Close
ToastMessageShow("Encryption Done", False)
End Sub
Sub DecryptFile (Path As String, Password As String)
Dim size As Long = File.Size(Path, "")
Dim rafInput, rafOutput As RandomAccessFile
rafInput.Initialize(Path, "", True)
rafOutput.Initialize(Path & ".decrypted", "", False)
Dim b() As Byte
Dim enc As B4XCipher
Do While rafInput.CurrentPosition < size
Dim blockLength As Int = rafInput.ReadInt(rafInput.CurrentPosition)
If b.Length <> blockLength Then
Dim b(blockLength) As Byte
End If
rafInput.ReadBytes(b, 0, b.Length, rafInput.CurrentPosition)
If Label8.Text.Length < 50 Then
For x = 0 To b.Length -1 'represent text file content
Label8.Text = Label8.Text & b(x) & ","
Next
End If
Dim bb() As Byte = enc.Decrypt(b, Password)
If Label10.Text.Length < 50 Then
For x = 0 To bb.Length -1 'represent text file content
Label10.Text = Label10.Text & bb(x) & ","
Next
End If
'rafOutput.WriteBytes(bb, 0, bb.Length, rafOutput.CurrentPosition)
ProgressBar1.Progress = rafInput.CurrentPosition / size * 100
Sleep(30)
Loop
rafInput.Close
rafOutput.Close

ToastMessageShow("DoneDecrypting", False)
'fx.Msgbox(MainForm, "Done!", "")
End Sub
 
Upvote 0

omo

Active Member
Licensed User
Longtime User
Whaoo! I don't know how to thank you enough. I am so grateful. I will try this one you sent
Regards
 
Upvote 0
Top