B4J Question MQTT with ca,cert & key file

Discussion in 'B4J Questions' started by madru, Nov 29, 2019.

Similar threads

B4A Tutorial [B4X] MQTT Chat Room
B4J Tutorial [IoT] MQTT Protocol
B4J Library MqttBroker
B4A Tutorial [B4X] MQTT - Connect & Reconnect
B4A Code Snippet [B4X] MQTT SSL and Self Signed Certificates
  1. madru

    madru Active Member Licensed User

    Good morning,

    I developed some software for a client in 2015 to fetch sensor data via MQTT and to do some analytics and graphical statistics with that data
    The main program is witten in B4J and the MQTT communication is done in Python. My client asked for some changes and I tried (for the sake of simplicity) to remove the Python part to do everything in B4J but I failed. I have absolutely no idea how to load the necessary ca,key,cer file and pass those to paho in B4J.

    can somebody point me into the right direction?

    THX



    this is how it is done in Python
    Code:
    cafile = os.path.join(certdir, "root_ca_auth.cer")
    certfile = os.path.join(certdir, 
    "device_auth.crt")
    keyfile = os.path.join(certdir, 
    "device_auth.key")

    mqttc = pahomqtt.Client(client_id=client_gen_id)
    mqttc.on_connect = on_connect
    mqttc.on_disconnect = on_disconnect
    mqttc.on_message = on_message
    mqttc.username_pw_set(cert_user, password=client_password)
    mqttc.tls_set(cafile, certfile, keyfile)
    mqttc.tls_insecure_set(
    True)
     
  2. inakigarm

    inakigarm Well-Known Member Licensed User

  3. Erel

    Erel Administrator Staff Member Licensed User

    Java code for client authentication is available here: https://gist.github.com/sharonbn/4104301
    It shouldn't be too complicated to integrate it with inline Java however it will only work if the various files are in the expected format.
     
    DonManfred likes this.
  4. DonManfred

    DonManfred Expert Licensed User

    really should be? So it is complicated?

    Edit to add: Erel has adapted his post ;-)
     
    Last edited: Dec 1, 2019 at 8:28 AM
    Erel likes this.
  5. madru

    madru Active Member Licensed User

    Hi,

    thx for the suggestion :)

    I modified the Git code to get it work with the actual bouncycastle. (I hope)

    I can compile it with javac without any problems, but I can't get it to work as inline code


    any ideas?

    THX

    Code:
    B4J Version: 8.00
    Java Version: 
    8
    Parsing code.    (
    0.03s)
    Building folders structure.    (
    0.37s)
    Compiling code.    (
    0.07s)
    Compiling layouts code.    (
    0.00s)
    Organizing libraries.    (
    0.00s)
    Compiling generated Java code.    Error
    B4J line: 
    15
    End Sub
    javac 
    1.8.0_152
    src\b4j\example\main.java:
    102: error: Illegal static declaration in inner class main.SslUtil
        static SSLSocketFactory getSocketFactory (final 
    String caCrtFile, final String crtFile, final String keyFile,
                                ^
      modifier 
    'static' is only allowed in constant variable declarations
    1 error
    Code:
    #Region Project Attributes 
        
    #MainFormWidth: 600
        
    #MainFormHeight: 600 
    #End Region

    Sub Process_Globals
        
    Private fx As JFX
        
    Private MainForm As Form
    End Sub

    Sub AppStart (Form1 As Form, Args() As String)
        MainForm = Form1
        
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
        MainForm.Show
    End Sub




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

    #If JAVA
    import java.io.*;
    import java.nio.file.*;
    import java.security.*;
    import java.security.cert.*;
    import javax.net.ssl.*;
    import org.bouncycastle.jce.provider.*;
    import org.bouncycastle.openssl.*;
    //import org.bouncycastle.openssl.PEMParser;
    import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
    import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;

    public class SslUtil
    {
        static SSLSocketFactory getSocketFactory (final String caCrtFile, final String crtFile, final String keyFile,
                final String password) throws Exception
        {
            Security.addProvider(new BouncyCastleProvider());
            // load CA certificate
            PEMParser reader = new PEMParser(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));
            reader.readObject();  // skip the curve definition
            X509Certificate caCert = (X509Certificate)reader.readObject();
            reader.close();

            // load client certificate
            //        reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
            reader = new PEMParser(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
            X509Certificate cert = (X509Certificate)reader.readObject();
            reader.close();

            // load client private key
            reader = new PEMParser(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))));
            Object object = (KeyPair)reader.readObject();
            PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
            KeyPair key;
            if (object instanceof PEMEncryptedKeyPair)
            {
                System.out.println("Encrypted key - we will use provided password");
                key = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv));
            }
            else
            {
                System.out.println("Unencrypted key - no password needed");
                key = converter.getKeyPair((PEMKeyPair) object);
            }
            reader.close();

            // CA certificate is used to authenticate server
            KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
            caKs.load(null, null);
            caKs.setCertificateEntry("ca-certificate", caCert);
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(caKs);
            // client key and certificates are sent to server so it can authenticate us
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(null, null);
            ks.setCertificateEntry("certificate", cert);
            ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[] {cert});
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, password.toCharArray());
            // finally, create SSL socket factory
            SSLContext context = SSLContext.getInstance("TLSv1");
            context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
            return context.getSocketFactory();
        }
    }

    #End If
     
  6. Erel

    Erel Administrator Staff Member Licensed User

    Please upload the project.
     
  7. madru

    madru Active Member Licensed User

    here are the few lines of the test project.

    Code:
    #Region Project Attributes 
        
    #MainFormWidth: 600
        
    #MainFormHeight: 600 
    #End Region

    Sub Process_Globals
        
    Private fx As JFX
        
    Private MainForm As Form
        
    Dim client As MqttClient
        
    Private Button_connect As Button
    End Sub

    Sub AppStart (Form1 As Form, Args() As String)
        MainForm = Form1
        MainForm.RootPane.LoadLayout(
    "Layout1"'Load the layout file.
        MainForm.Show
       
        
    'Initial cmqtt client
        client.Initialize("client""tcp://192.168.1.10:1883""myClientName")
       
        
    '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 Keep avlive & Timeout
        Dim mo As MqttConnectOptions
        mo.Initialize(
    "user","pass")
        
    Dim result As String
        
    Dim MqttConnectOptions1 As JavaObject = mo
        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))
        
    Log(jo.RunMethod("setSocketFactory",jo.RunMethod("getSocketFactory"Array As String (File.GetUri(File.DirAssets, "root_ca_auth.cer"), File.GetUri(File.DirAssets, "sensor_auth.crt"), File.GetUri(File.DirAssets, "sensor_auth.key"), "pass"))))
        
    'result = MqttConnectOptions1.RunMethod("setSocketFactory",input from SslUtil)
    End Sub


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


        
    If MethodName = "messageArrived" Then
            
    Log("Message Arrived!")
            
    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

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

    #If JAVA
    //import java.lang.reflect.InvocationTargetException;
    import java.io.*;
    import java.nio.file.*;
    import java.security.*;
    import java.security.cert.*;
    import javax.net.ssl.*;
    import org.bouncycastle.jce.provider.*;
    import org.bouncycastle.openssl.*;
    //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) throws Exception
        {
            Security.addProvider(new BouncyCastleProvider());
            // load CA certificate
            PEMParser reader = new PEMParser(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));
            reader.readObject();  // skip the curve definition
            X509Certificate caCert = (X509Certificate)reader.readObject();
            reader.close();

            // load client certificate
            //      reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
            reader = new PEMParser(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
            X509Certificate cert = (X509Certificate)reader.readObject();
            reader.close();

            // load client private key
            reader = new PEMParser(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))));
            Object object = (KeyPair)reader.readObject();
            PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
            KeyPair key;
            if (object instanceof PEMEncryptedKeyPair)
            {
                System.out.println("Encrypted key - we will use provided password");
                key = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv));
            }
            else
            {
                System.out.println("Unencrypted key - no password needed");
                key = converter.getKeyPair((PEMKeyPair) object);
            }
            reader.close();

            // CA certificate is used to authenticate server
            KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
            caKs.load(null, null);
            caKs.setCertificateEntry("ca-certificate", caCert);
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(caKs);
            // client key and certificates are sent to server so it can authenticate us
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(null, null);
            ks.setCertificateEntry("certificate", cert);
            ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[] {cert});
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, password.toCharArray());
            // finally, create SSL socket factory
            SSLContext context = SSLContext.getInstance("TLSv1");
            context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
            return context.getSocketFactory();
        }

    }
    #End If
     
  8. Erel

    Erel Administrator Staff Member Licensed User

    Help us help you. Upload the project.
     
  9. madru

    madru Active Member Licensed User

    the project without CA etc.
     

    Attached Files:

  10. madru

    madru Active Member Licensed User

    getting closer......

    Java code runs without any error now and outputs correct values


    Code:
    #If JAVA
    //import java.lang.reflect.InvocationTargetException;
    import java.io.*;
    import java.nio.file.*;
    import java.security.*;
    import java.security.cert.*;
    import javax.net.ssl.*;
    import org.bouncycastle.jce.provider.*;
    import org.bouncycastle.openssl.*;
    //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)
        throws Exception
        {
            Security.addProvider(new BouncyCastleProvider());

            // load CA certificate
            X509Certificate caCert = null;

            FileInputStream fis = new FileInputStream(caCrtFile);
            BufferedInputStream bis = new BufferedInputStream(fis);
            CertificateFactory cf = CertificateFactory.getInstance("X.509");

            while (bis.available() > 0)
            {
                caCert = (X509Certificate) cf.generateCertificate(bis);
                System.out.println(caCert.toString());
            }

            // load client certificate
            bis = new BufferedInputStream(new FileInputStream(crtFile));
            X509Certificate cert = null;
            while (bis.available() > 0)
            {
                cert = (X509Certificate) cf.generateCertificate(bis);
                System.out.println(caCert.toString());
            }

            // load client private key
            PEMParser pemParser = new PEMParser(new FileReader(keyFile));
            Object object = pemParser.readObject();
            PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder()
            .build(password.toCharArray());
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter()
            .setProvider("BC");
            KeyPair key;
            if (object instanceof PEMEncryptedKeyPair)
            {
                System.out.println("Encrypted key - we will use provided password");
                key = converter.getKeyPair(((PEMEncryptedKeyPair) object)
                                           .decryptKeyPair(decProv));
            }
            else
            {
                System.out.println("Unencrypted key - no password needed");
                key = converter.getKeyPair((PEMKeyPair) object);
            }
            pemParser.close();

            // CA certificate is used to authenticate server
            KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
            caKs.load(null, null);
            caKs.setCertificateEntry("ca-certificate", caCert);
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
            tmf.init(caKs);

            // client key and certificates are sent to server so it can authenticate
            // us
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(null, null);
            ks.setCertificateEntry("certificate", cert);
            ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(),
                           new java.security.cert.Certificate[] { cert });
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
                                    .getDefaultAlgorithm());
            kmf.init(ks, password.toCharArray());

            // finally, create SSL socket factory
            SSLContext context = SSLContext.getInstance("TLSv1.2");
            context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

            return context.getSocketFactory();
        }

    }
    #End If

    but getting now:

    Code:
    java.lang.ClassCastException: sun.security.ssl.SSLSocketFactoryImpl cannot be cast to [Ljava.lang.Object;
        at b4j.example.main._appstart(main.java:
    158)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
    62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
    43)
        at java.lang.reflect.Method.invoke(Method.java:
    498)
        at anywheresoftware.b4a.shell.Shell.runMethod(
    Shell.java:632)
        at anywheresoftware.b4a.shell.Shell.raiseEventImpl(
    Shell.java:237)
        at anywheresoftware.b4a.shell.Shell.raiseEvent(
    Shell.java:167)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
    62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
    43)
        at java.lang.reflect.Method.invoke(Method.java:
    498)
        at anywheresoftware.b4a.BA.raiseEvent2(BA.java:
    91)
        at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:
    98)
        at anywheresoftware.b4a.BA.raiseEvent(BA.java:
    78)
        at b4j.example.main.start(main.java:
    47)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$
    161(LauncherImpl.java:863)
        at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$
    174(PlatformImpl.java:326)
        at com.sun.javafx.application.PlatformImpl.lambda$
    null$172(PlatformImpl.java:295)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.application.PlatformImpl.lambda$runLater$
    173(PlatformImpl.java:294)
        at com.sun.glass.ui.InvokeLaterDispatcher$
    Future.run(InvokeLaterDispatcher.java:95)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$
    null$147(WinApplication.java:177)
        at java.lang.Thread.run(
    Thread.java:748)
     
  11. Erel

    Erel Administrator Staff Member Licensed User

    I cannot help you like this. Sorry.
     
  12. Daestrum

    Daestrum Well-Known Member Licensed User

    Try changing (not 100% sure it's the problem)

    Code:
    Log(jo.RunMethod("setSocketFactory",jo.RunMethod("getSocketFactory"Array As String (File.GetUri(File.DirAssets, "root_ca_auth.cer"), File.GetUri(File.DirAssets, "sensor_auth.crt"), File.GetUri(File.DirAssets, "sensor_auth.key"), "pass"))))
    to - (Array added before the getSocketFactory method call)
    Code:
    Log(jo.RunMethod("setSocketFactory",Array(jo.RunMethod("getSocketFactory"Array As String (File.GetUri(File.DirAssets, "root_ca_auth.cer"), File.GetUri(File.DirAssets, "sensor_auth.crt"), File.GetUri(File.DirAssets, "sensor_auth.key"), "pass")))))
     
  13. madru

    madru Active Member Licensed User

    THX Daestrum, yes missed the Array
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice