B4J Question [solved] problem client server solution with ssl

MarkusR

Well-Known Member
Licensed User
so i made a server in b4j with jServer (see #3)
it get a request and it send a list with userdefined type as bytes as response.
that works fine if me test this from browser.

now i try to make a b4j app that make this get request vis https:// + username & password
but i get a error here :(

Client Source Code
B4X:
#Region Project Attributes
    #MainFormWidth: 1024
    #MainFormHeight: 768
    'Encrypt / Decrypt
    #AdditionalJar: bcprov-jdk15on-154
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Type SinglePassword(Id As Int,Name As String,User As String,Url As String,Password() As Byte,Changed As Long,Source As String,Mail As String,Note As String)
 
End Sub

Sub AppStart (Form1 As Form, Args() As String)

    DateTime.DateFormat = "dd.MM.yyyy HH:mm:ss"
     
    MainForm = Form1
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
    MainForm.Show
 
    Request
 
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

Sub Request()
 
    Dim Job As HttpJob
    Job.Initialize("",Me)
    Job.Username="abc"
    Job.Password="123"
    Job.Download2("https://any.ddns.net/request", Array As String("cmd", "list", "filter", ""))

    Dim ser As B4XSerializator
 
    Wait For (Job) JobDone(Job As HttpJob)
    If Job.Success Then
        Dim buffer(Job.GetInputStream.BytesAvailable) As Byte
        Dim List1 As List
        Job.GetInputStream.ReadBytes(buffer, 0, buffer.length)
        List1 = ser.ConvertBytesToObject(buffer)
        For Each Item As SinglePassword In List1
            Log(Item.Id & " " & Item.Name)
        Next
    End If
    Job.Release
 
End Sub
Client Error from Sub Request (i test Server & Client at same PC)
something with PKIX (public key infrastructure exchange)
Waiting for debugger to connect...
Program started.
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.ssl.Alerts.getSSLException(Alerts.java:198)
at java.base/sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1969)
at java.base/sun.security.ssl.Handshaker.fatalSE(Handshaker.java:345)
at java.base/sun.security.ssl.Handshaker.fatalSE(Handshaker.java:339)
at java.base/sun.security.ssl.ClientHandshaker.checkServerCerts(ClientHandshaker.java:1968)
at java.base/sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1777)
at java.base/sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:264)
at java.base/sun.security.ssl.Handshaker.processLoop(Handshaker.java:1092)
at java.base/sun.security.ssl.Handshaker.processRecord(Handshaker.java:1026)
at java.base/sun.security.ssl.SSLSocketImpl.processInputRecord(SSLSocketImpl.java:1137)
at java.base/sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1074)
at java.base/sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
at java.base/sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1402)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1429)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:242)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:200)
at okhttp3.internal.connection.RealConnection.buildConnection(RealConnection.java:174)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:114)
at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:196)
at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:132)
at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:101)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:179)
at okhttp3.RealCall.execute(RealCall.java:63)
at anywheresoftware.b4h.okhttp.OkHttpClientWrapper.executeWithTimeout(OkHttpClientWrapper.java:156)
at anywheresoftware.b4h.okhttp.OkHttpClientWrapper.access$0(OkHttpClientWrapper.java:153)
at anywheresoftware.b4h.okhttp.OkHttpClientWrapper$ExecuteHelper.run(OkHttpClientWrapper.java:201)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:514)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:290)
at java.base/sun.security.validator.Validator.validate(Validator.java:264)
at java.base/sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:343)
at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:226)
at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:133)
at java.base/sun.security.ssl.ClientHandshaker.checkServerCerts(ClientHandshaker.java:1947)
... 38 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
... 44 more
ResponseError. Reason: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target, Response:
 
Last edited:

MarkusR

Well-Known Member
Licensed User
How did you configure ssl? Have you tried with plain http instead of https?
i need ssl, the server did only accept ssl because filter.

server main
B4X:
'Non-UI application (console / server application)
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
    'SQLite Database Access
    #AdditionalJar: sqlite-jdbc-3.7.2
    'Encrypt / Decrypt 
    #AdditionalJar: bcprov-jdk15on-154 
#End Region

Sub Process_Globals
    Private srvr As Server
End Sub

Sub AppStart (Args() As String)

    DateTime.DateFormat = "dd.MM.yyyy HH:mm:ss"
      
    'http://wiki.eclipse.org/Jetty/Howto/Configure_SSL#Generating_Keys_and_Certificates_with_JDK_keytool

    Log(File.DirApp)

    'erel: The keystore is only used to store the server certificate (and keys). It is not related to any other authentication mechanism that you use.

    'http://www.selfsignedcertificate.com/

    srvr.Initialize("srvr")
    '-------------------
    Dim ssl As SslConfiguration
    ssl.Initialize
    ssl.SetKeyStorePath(File.DirApp, "server.keystore") 'path to keystore file
    ssl.KeyStorePassword = "123456"
    'ssl.KeyManagerPassword = "654321"
    srvr.SetSslConfiguration(ssl, 443)
  
    srvr.AddFilter("/*", "BasicAuthentication", False)
    '-------------------
    srvr.AddDoSFilter("/*",Null) ' http://www.eclipse.org/jetty/documentation/9.4.x/dos-filter.html
    'srvr.LogsFileFolder = File.Combine(File.DirApp, "logs")
      
    srvr.StaticFilesFolder = File.Combine(File.DirApp, "www")
    'srvr.SetStaticFilesOptions(xx)
    srvr.AddHandler("/request", "Request", False)
    srvr.Start
    StartMessageLoop
  
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub
BasicAuthentication
B4X:
'Filter class
Sub Class_Globals
  
End Sub

Public Sub Initialize
  
End Sub

'Return True to allow the request to proceed.
Public Sub Filter(req As ServletRequest, resp As ServletResponse) As Boolean
  
    If req.Secure=False Then Return False
  
    If req.GetSession.GetAttribute2("logged in", False) = True Then Return True

    Dim auths As List = req.GetHeaders("Authorization")
    If auths.Size = 0 Then
        resp.SetHeader("WWW-Authenticate", $"Basic realm="Realm""$)
        resp.SendError(401, "authentication required")
        Return False
    Else
        If CheckCredentials(auths.Get(0)) Then
            req.GetSession.SetAttribute("logged in", True)
            Return True
        Else
            resp.SendError(401, "authentication required")
            Return False
        End If
    End If
  
End Sub

'using jStringUtils library
Private Sub CheckCredentials (auth As String) As Boolean
    Dim success As Boolean = False
    If auth.StartsWith("Basic") = True Then
        Dim b64 As String = auth.SubString("Basic ".Length)
        Dim su As StringUtils
        Dim b() As Byte = su.DecodeBase64(b64)
        Dim raw As String = BytesToString(b, 0, b.Length, "utf8")
        Dim UsernameAndPassword() As String = Regex.Split(":", raw)
        If UsernameAndPassword.Length = 2 Then
            'up to you to decide which credentials are allowed <---------------------------
            If UsernameAndPassword(0) = "abc" And UsernameAndPassword(1) = "123" Then
                success = True
            End If
        End If
    End If
    Return success
End Sub
Server Request Class
B4X:
'Handler class
Sub Class_Globals
  
End Sub

Public Sub Initialize
  
End Sub

Sub Handle(req As ServletRequest, resp As ServletResponse)

    Log(req.FullRequestURI)
    Log(req.ContentType) 

    Dim Manager As PasswordManager
    Manager.Initialize
  
    Dim ser As B4XSerializator
  
    Select req.GetParameter("cmd")
        Case "list"
            Dim List1 As List
            List1 = Manager.LoadAll("")        ' a list of -> Type SinglePassword(Id As Int,Name As String,User As String,Url As String,Password() As Byte,Changed As Long,Source As String,Mail As String,Note As String)     
                Dim buffer() As Byte
                buffer = ser.ConvertObjectToBytes(List1)
                resp.ContentType="application/octet-stream"
                resp.OutputStream.WriteBytes(buffer,0,buffer.Length)
                'Dim List2 As List
                'List2=ser.ConvertBytesToObject(buffer)
                'Log(List2.Size)
        Case Else
    End Select
      
End Sub
 

MarkusR

Well-Known Member
Licensed User
i give up for today, keytool did not import my file^^
but it seems i have 3 keystores, one for my server app, one in jdk and one in jre.
should %JAVA_HOME% point to jdk (dev kit) or jre (runtime enviroment) ?

and btw i hate :mad: commandline apps, exist a gui tool for keytool.exe?

D:\Java\jdk-9.0.4\bin\keytool
D:\Java\jdk-9.0.4\lib\security\cacerts

C:\Program Files\Java\jre-9.0.4\bin\keytool
C:\Program Files\Java\jre-9.0.4\lib\security\cacerts
 

Erel

Administrator
Staff member
Licensed User
Are you using a trusted certificate or a private signed one?
 

Erel

Administrator
Staff member
Licensed User
It is not a matter of risk. The client will throw an error if the certificate is not recognized. You need to initialize OkHttpClient with InitializeAcceptAll (you will need to use OkHttpUtils2 source code instead of the library).
 

MarkusR

Well-Known Member
Licensed User
With erels help i solved this in this steps
removeing jOkHttpUtils2 in library Manager
add in library Manager OkHttp 1.20
adding the source from HttpJob and HttpUtils2Service
change a row here in HttpUtils2Service, that was the key to success
B4X:
Sub Service_Create
 
#if B4A
    TempFolder = File.DirInternalCache
    Try
        File.WriteString(TempFolder, "~test.test", "test")
        File.Delete(TempFolder, "~test.test")
    Catch
        Log(LastException)
        Log("Switching to File.DirInternal")
        TempFolder = File.DirInternal
    End Try
#Else If B4J
    TempFolder = File.DirTemp
#End If
    'hc.Initialize("hc")
    hc.InitializeAcceptAll("hc") '<- MR 28.08.2018
    TaskIdToJob.Initialize
End Sub
@Erel,
would be nice if u can provide this in any way direct in this libs comes with ide :)
 
Top