B4J Question Sending/receiving encrypted string with OkHttpUtils2

walt61

Active Member
Licensed User
Longtime User
Hi all,

I've been trying to get my head around this and couldn't think of an elegant solution (probably caused by a lack of knowledge of most things http). I want to be able to send (from B4J client to B4J server) a string - actually a byte array of course - generated with B4XCipher's Encrypt method, from a client to a web server (that communication channel is a must).

Which jOkHttpUtils2 (or other if I'm barking up the wrong tree) objects/methods should I use on the sending and receiving side as on the receiving side I obviously want to be able to decrypt the received data?

Thanks in advance,

walt61
 

fgrdovic21

Member
Licensed User
Longtime User
I've spent several days trying to figure that out myself. The forum has great tutorial on each part of the process, it was just a matter of combining them all together.
Here is the code I use:

Client:
B4X:
'Class module
Sub Class_Globals
    Private pass As String = "b0T!bn3%Ft0,zBrJ!" 'Make sure your password is in Class_Globals so it can be obfuscated
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
 
End Sub

Private Sub SendRequest
    Dim j As HttpJob
    j.Initialize("", Me) 'Change 'Me' to any class that will handle the JobDone event (make sure that class IS NOT a Code Module)
    j.GetRequest.Timeout = 5000
    j.PostBytes("http://www.yoururl.com/yourpage", EncryptAES("yourrequestdata").GetBytes("UTF8"))
End Sub

Private Sub JobDone(j As HttpJob)
    If j.Success Then
        Dim res As String = ExtractRequestData(j.GetInputStream)
        'Do something with res
    End If
End Sub



Public Sub ExtractRequestData (Input As InputStream) As String
    Dim res As String = DecryptAES(ReadStreamBytes(Input))
    Return res
End Sub

Public Sub ReadStreamBytes(Input As InputStream) As String
    Dim buffer As OutputStream
    buffer.InitializeToBytesArray(0)
    File.Copy2(Input, buffer)
    Dim raf As RandomAccessFile
    raf.Initialize3(buffer.ToBytesArray, False)
    Dim b(2048) As Byte 'Maximum size of the received data
    raf.ReadBytes(b, 0, raf.Size, 0)
    Dim res As String = BytesToString(b, 0, raf.CurrentPosition, "UTF8")
    raf.Close
    Return res
End Sub



Public Sub EncryptAES (Data As String) As String
    Dim bc As ByteConverter
    Dim aes As B4XCipher
    Return bc.HexFromBytes(aes.Encrypt(Data.GetBytes("UTF8"), pass))
End Sub

Public Sub DecryptAES (Data As String) As String
    Dim aes As B4XCipher
    Dim bc As ByteConverter
    Dim res() As Byte = aes.Decrypt(bc.HexToBytes(Data), pass)
    Return BytesToString(res, 0, res.Length, "UTF8")
End Sub

Server:
B4X:
'Handler class
Sub Class_Globals
    Private pass As String = "b0T!bn3%Ft0,zBrJ!" 'Make sure your password is in Class_Globals so it can be obfuscated
End Sub

Public Sub Initialize
 
End Sub

Sub Handle(req As ServletRequest, resp As ServletResponse)
    Dim res As String = ExtractRequestData(req)
    If res = "yourrequestdata" Then
        Respond("yourresponsedata", resp)
    End If
End Sub

Public Sub ExtractRequestData (req As ServletRequest) As String
    Dim res As String = DecryptAES(ReadStreamBytes(req.InputStream))
    Return res
End Sub

Public Sub Respond (Data As String, Resp As ServletResponse)
    WriteStreamBytes(EncryptAES(Data).GetBytes("UTF8"), Resp.OutputStream)
End Sub



Public Sub ReadStreamBytes(Input As InputStream) As String
    Dim buffer As OutputStream
    buffer.InitializeToBytesArray(0)
    File.Copy2(Input, buffer)
    Dim raf As RandomAccessFile
    raf.Initialize3(buffer.ToBytesArray, False)
    Dim b(2048) As Byte 'Maximum size of the received data
    raf.ReadBytes(b, 0, raf.Size, 0)
    Dim res As String = BytesToString(b, 0, raf.CurrentPosition, "UTF8")
    raf.Close
    Return res
End Sub

Public Sub WriteStreamBytes(Data() As Byte, Output As OutputStream)
    Output.WriteBytes(Data, 0, Data.Length)
End Sub



Public Sub EncryptAES (Data As String) As String
    Dim bc As ByteConverter
    Dim aes As B4XCipher
    Return bc.HexFromBytes(aes.Encrypt(Data.GetBytes("UTF8"), pass))
End Sub

Public Sub DecryptAES (Data As String) As String
    Dim aes As B4XCipher
    Dim bc As ByteConverter
    Dim res() As Byte = aes.Decrypt(bc.HexToBytes(Data), pass)
    Return BytesToString(res, 0, res.Length, "UTF8")
End Sub

Let me walk you through the whole process (Bold text are subs in the code)

Client (your B4J, B4A, B4I app) SendsRequest to your B4J server. Server Handles your request and ExtractsRequestedData. That data is then read via ReadStreamBytes and decrypted via DecryptAES. It is returned to the Handle sub (where you can do with whatever you want). Server then Responds to your Request,
where it EncryptsAES your data and WritesStreamBytes to the Output stream. The Client receives data in the JobDone sub where it ExtractsRequestData from the InputStream (which is the OutputStream from the Server). In that ExtractRequestData sub, data is firstly read via ReadStreamBytes and then DecryptAES. Then you can do whatever you want with it.

Here are that steps in a more general way: RequestData is encrypted, transformed into byte array and sent to the server. Server transforms that byte array into string and decrypts it. Server then responds by encrypting ResponseData and transforming it into byte array which is sent to the client. The Client receives that data, transforms it from byte array to string and then decrypts it. I think you can see the pattern that repeats here.

Here are some things to keep in mind:
  1. EncryptAES and DecryptAES are same for the Server and the Client
  2. Obviously, password used for encrypting and decrypting must be the same and stored in Class_Globals so it can be obfuscated
  3. ReadStreamBytes is also the same for the server and the client (but keep in mind the maximum size of the bytes you are sending and receiving)
  4. Make sure that the TargetModule in the Initialization method of the HttpJob is set to the class that will handle the JobDone event (make sure it is not a Code Module)
  5. Instead of sending string, I recommend sending JSON
  6. Libraries used: ByteConverter, jB4XEncryption, jOkHttpUtils2 (or jOkHttpUtils2_NONUI if using console application), jRandomAccessFile, jServer, OkHttp (this is used for the Clients 'j.GetRequest.Timeout' which I recommend using)
  7. The Client can also be very easily transformed into a B4A app (or maybe even a B4I app, I don't know, haven't used it)
  8. I recommend grouping these Subs in the following modules: Code Module > Server (ReadStreamBytes, WriteStreamBytes, ExtractRequestData, Respond), Code Module > Cypher (EncryptAES, DecryptAES)
The code I took this from was used with JSON and Maps, so I transformed it for string. It wasn't tested, so if it doesn't work, don't be afraid to ask me for help.
 
Last edited:
Upvote 0

fgrdovic21

Member
Licensed User
Longtime User
EDIT: Now that I think of it, it probably will not work with sending strings. So, just in case, I'm providing you the JSON and Map method. It requires the Json library.

Client:
B4X:
'Handler class
Sub Class_Globals
    Private pass As String = "b0T!bn3%Ft0,zBrJ!" 'Make sure your password is in Class_Globals so it can be obfuscated
End Sub

Public Sub Initialize
   
End Sub

Sub Handle(req As ServletRequest, resp As ServletResponse)
    Dim m As Map = ExtractRequestData(req)
    Dim value As String = m.Get("key")
    If value = "value" Then
        Dim res As Map
        res.Initialize
        res.Put("RespKey", "RespValue")
        Respond(res, resp)
    End If
End Sub

Public Sub ExtractRequestData (req As ServletRequest) As Map
    Dim res As String = DecryptAES(ReadStreamBytes(req.InputStream))
    Dim json As JSONParser
    json.Initialize(res)
    Dim m As Map
    m = json.NextObject
    Return m
End Sub

Public Sub Respond (Data As Map, Resp As ServletResponse)
    Dim json As JSONGenerator
    json.Initialize(Data)
    WriteStreamBytes(EncryptAES(json.ToString).GetBytes("UTF8"), Resp.OutputStream)
End Sub



Public Sub ReadStreamBytes(Input As InputStream) As String
    Dim buffer As OutputStream
    buffer.InitializeToBytesArray(0)
    File.Copy2(Input, buffer)
    Dim raf As RandomAccessFile
    raf.Initialize3(buffer.ToBytesArray, False)
    Dim b(2048) As Byte 'Maximum size of the received data
    raf.ReadBytes(b, 0, raf.Size, 0)
    Dim res As String = BytesToString(b, 0, raf.CurrentPosition, "UTF8")
    raf.Close
    Return res
End Sub

Public Sub WriteStreamBytes(Data() As Byte, Output As OutputStream)
    Output.WriteBytes(Data, 0, Data.Length)
End Sub



Public Sub EncryptAES (Data As String) As String
    Dim bc As ByteConverter
    Dim aes As B4XCipher
    Return bc.HexFromBytes(aes.Encrypt(Data.GetBytes("UTF8"), pass))
End Sub

Public Sub DecryptAES (Data As String) As String
    Dim aes As B4XCipher
    Dim bc As ByteConverter
    Dim res() As Byte = aes.Decrypt(bc.HexToBytes(Data), pass)
    Return BytesToString(res, 0, res.Length, "UTF8")
End Sub

Server:
B4X:
'Class module
Sub Class_Globals
    Private pass As String = "b0T!bn3%Ft0,zBrJ!" 'Make sure your password is in Class_Globals so it can be obfuscated
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
   
End Sub

Private Sub SendRequest
    Dim req As Map
    req.Initialize
    req.Put("key", "value")
   
    Dim json As JSONGenerator
    json.Initialize(req)
   
    Dim res As String = EncryptAES(json.ToString())
   
    Dim j As HttpJob
    j.Initialize("", Me) 'Change 'Me' to any class that will handle the JobDone event (make sure that class IS NOT a Code Module)
    j.GetRequest.Timeout = 5000
    j.PostBytes("http://www.yoururl.com/yourpage", res.GetBytes("UTF8"))
End Sub

Private Sub JobDone(j As HttpJob)
    If j.Success Then
        Dim res As Map = ExtractRequestData(j.GetInputStream)
        Dim val As String = res.Get("RespKey")
        If val = "RespValue" Then
           
        End If
        'Do something with res
    End If
End Sub



Public Sub ExtractRequestData (Input As InputStream) As Map
    Dim res As String = DecryptAES(ReadStreamBytes(Input))
    Dim json As JSONParser
    json.Initialize(res)
    Dim m As Map
    m = json.NextObject
    Return m
End Sub

Public Sub ReadStreamBytes(Input As InputStream) As String
    Dim buffer As OutputStream
    buffer.InitializeToBytesArray(0)
    File.Copy2(Input, buffer)
    Dim raf As RandomAccessFile
    raf.Initialize3(buffer.ToBytesArray, False)
    Dim b(2048) As Byte 'Maximum size of the received data
    raf.ReadBytes(b, 0, raf.Size, 0)
    Dim res As String = BytesToString(b, 0, raf.CurrentPosition, "UTF8")
    raf.Close
    Return res
End Sub



Public Sub EncryptAES (Data As String) As String
    Dim bc As ByteConverter
    Dim aes As B4XCipher
    Return bc.HexFromBytes(aes.Encrypt(Data.GetBytes("UTF8"), pass))
End Sub

Public Sub DecryptAES (Data As String) As String
    Dim aes As B4XCipher
    Dim bc As ByteConverter
    Dim res() As Byte = aes.Decrypt(bc.HexToBytes(Data), pass)
    Return BytesToString(res, 0, res.Length, "UTF8")
End Sub
 
Upvote 0
Top