B4J Question Secure MQTT client based on socket factory

paddy12309

Member
Licensed User
Hi everyone,

I have a large program running well except for when I try to implement the socket factory example of MQTT into the program. I get the following error (I have stripped this back to the exact example of socket factory, which I have running in itself.)

this is the following error I get:

error:
Error occurred on line: 11476 (Main)
java.lang.RuntimeException: java.net.SocketException: Socket closed
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:140)
    at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:100)
    at anywheresoftware.b4a.BA$3.run(BA.java:267)
    at anywheresoftware.b4a.keywords.SimpleMessageLoop.runMessageLoop(SimpleMessageLoop.java:47)
    at anywheresoftware.b4a.StandardBA.startMessageLoop(StandardBA.java:43)
    at anywheresoftware.b4a.shell.ShellBA.startMessageLoop(ShellBA.java:121)
    at anywheresoftware.b4a.keywords.Common.StartMessageLoop(Common.java:180)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:309)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(
Method.java:566)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:111)
    at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:100)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:98)
    at b4j.collator1.main.main(main.java:51)
Caused by: java.net.SocketException: Socket closed
    at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:113)
    at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
    at java.base/java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:81)
    at java.base/java.io.BufferedOutputStream.flush(BufferedOutputStream.java:142)
    at anywheresoftware.b4a.shell.ShellConnector.sendControlMessage(ShellConnector.java:55)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:189)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
    at jdk.internal.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base
/java.lang.reflect.Method.invoke(Method.java:566)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:111)
    ... 16 more
java.lang.RuntimeException: java.util.NoSuchElementException
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:140)
    at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:100)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:98)
    at b4j.collator1.main.main(main.java:51)

this is the code I have:
Socket factory implemented code:
Sub MQTTStart
    'MQTT
    Dim cafile As String = File.GetUri (File.DirAssets, "mqtt-ca.crt").SubString(6)
    Dim certfile As String = File.GetUri(File.DirAssets, "client-11.crt").SubString(6)
    Dim keyfile  As String = File.GetUri(File.DirAssets, "client-11-nopassword.key").SubString(6)

    Dim url As String ="ssl://mqtt.1.smarttwin.app:8883"
    Dim clientId As String = "Test"
    Dim user As String = "password"
    Dim password As String = "password"
    
    Dim keyFilePassword As String = Null
    
    
    'Initial cmqtt client
    client.Initialize("client", url, clientId)
    
    'setup Paho MqttCallbackExtended
    Dim Mjo As JavaObject = client
    Dim event As Object = Mjo.CreateEventFromUI("org.eclipse.paho.client.mqttv3.MqttCallback", "MqttCallback", Null)
    Mjo.GetFieldJO("client").RunMethod("setCallback", Array(event))
    
    'set Paho Options
    mo.Initialize(user,password)
    Dim result As String
    Dim MqttConnectOptions1 As JavaObject = mo
    result = MqttConnectOptions1.RunMethod("setMqttVersion",Array(3))
    result = MqttConnectOptions1.RunMethod("setKeepAliveInterval",Array(60))
    result = MqttConnectOptions1.RunMethod("setConnectionTimeout",Array(60))

    'setup SocketFactory => does not work
    Dim jo As JavaObject = Me
    jo.InitializeNewInstance("b4j.example.main.SslUtil", Array(Null))
    MqttConnectOptions1.RunMethod("setSocketFactory",Array(jo.RunMethod("getSocketFactory", Array As String (cafile, certfile, keyfile, keyFilePassword))))

    ConnectMe
End Sub

Sub ConnectMe
    
    client.Connect2(mo)
End Sub

Private Sub client_Connected (Success As Boolean)
    If Success Then
        Log("Client Connected")
        client.Subscribe("#",0)
    Else
        Log("Connection Failed")
    End If
    
End Sub
Private Sub client_Disconnected
    
    Log("Client Disconnected")
    
End Sub

private Sub MqttCallback_Event (MethodName As String, Args() As Object ) As Object

 
    If MethodName = "messageArrived" Then
        'Log("Message Arrived!")
        handleMessageArrived(Args)
        Return Null
    else If MethodName = "deliveryComplete" Then
        Log("Delivery Complete!")
        Return Null
    else If MethodName = "connectionLost" Then
        Log("Connection lost!")
        Return Null
    End If
    
End Sub

Sub handleMessageArrived(args() As Object)
    
    Dim topic As String = args(0)
    Dim mA As JavaObject = args(1)
    Dim payload() As Byte = mA.RunMethod("getPayload", Null)
    If payload.Length > 0 Then
        Log(topic & " ->  " & (BytesToString(payload, 0, payload.Length, "utf8").Trim))
    End If
    
End Sub



#If JAVA
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileReader;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileReader;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;

public class SslUtil
{
    public  SSLSocketFactory getSocketFactory(final String caCrtFile, final String crtFile, final String keyFile,
                                                    final String password) {
        try {

            /**
             * Add BouncyCastle as a Security Provider
             */
            Security.addProvider(new BouncyCastleProvider());

            JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter().setProvider("BC");

            /**
             * Load Certificate Authority (CA) certificate
             */
            PEMParser reader = new PEMParser(new FileReader(caCrtFile));
            X509CertificateHolder caCertHolder = (X509CertificateHolder) reader.readObject();
            reader.close();

            X509Certificate caCert = certificateConverter.getCertificate(caCertHolder);

            /**
             * Load client certificate
             */
            reader = new PEMParser(new FileReader(crtFile));
            X509CertificateHolder certHolder = (X509CertificateHolder) reader.readObject();
            reader.close();

            X509Certificate cert = certificateConverter.getCertificate(certHolder);

            /**
             * Load client private key
             */
            reader = new PEMParser(new FileReader(keyFile));
            Object keyObject = reader.readObject();
            reader.close();

            PEMDecryptorProvider provider = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
            JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter().setProvider("BC");

            KeyPair key;

            if (keyObject instanceof PEMEncryptedKeyPair) {
                key = keyConverter.getKeyPair(((PEMEncryptedKeyPair) keyObject).decryptKeyPair(provider));
            } else {
                key = keyConverter.getKeyPair((PEMKeyPair) keyObject);
            }

            /**
             * CA certificate is used to authenticate server
             */
            KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            caKeyStore.load(null, null);
            caKeyStore.setCertificateEntry("ca-certificate", caCert);

            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                    TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(caKeyStore);

            /**
             * Client key and certificates are sent to server so it can authenticate the client
             */
            KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            clientKeyStore.load(null, null);
            clientKeyStore.setCertificateEntry("certificate", cert);
            clientKeyStore.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(),
                    new Certificate[]{cert});

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
                    KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(clientKeyStore, password.toCharArray());

            /**
             * Create SSL socket factory
             */
            SSLContext context = SSLContext.getInstance("TLSv1.2");
            context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

            /**
             * Return the newly created socket factory object
             */
            return context.getSocketFactory();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

}
#End If

I'm out of ideas on things to try in relation to making this work, I have the same additional jars, and package properties as in my socket factory example.
#AdditionalJar: bcprov-jdk18on-172
#AdditionalJar: bcpkix-jdk15to18-172
#PackagerProperty: VMArgs = --add-opens b4j/org.bouncycastle.jcajce.provider.asymmetric.x509=java.base

any help or direction would be greatly appreciated!
 

aminoacid

Active Member
Licensed User
Longtime User

Can you provide a link to the "socket factory example" you are referring to?
 
Upvote 0
Cookies are required to use this site. You must accept them to continue using the site. Learn more…