This is an automated email from the ASF dual-hosted git repository.

chrisdutz pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 1e0eaaf2c3bcacd976658a87ed4b3497d687226d
Author: Christofer Dutz <[email protected]>
AuthorDate: Tue Jun 23 19:14:10 2026 +0200

    feat: Updated the plc4x server and driver to operate via TLS and to support 
authentication.
---
 RELEASE_NOTES                                      |  16 +++
 plc4j/drivers/plc4x/pom.xml                        |   5 +
 .../java/plc4x/readwrite/Plc4xAuthRequest.java     | 149 +++++++++++++++++++++
 .../java/plc4x/readwrite/Plc4xAuthResponse.java    | 106 +++++++++++++++
 .../plc4x/java/plc4x/readwrite/Plc4xMessage.java   |   6 +-
 .../java/plc4x/readwrite/Plc4xRequestType.java     |   6 +-
 .../apache/plc4x/java/plc4x/Plc4xConnection.java   |  45 +++++++
 .../org/apache/plc4x/java/plc4x/Plc4xDriver.java   |  11 +-
 .../apache/plc4x/java/plc4x/Plc4xMessageCodec.java |  29 ++++
 .../java/plc4x/config/Plc4xConfiguration.java      |  24 ++++
 .../config/Plc4xTlsTransportConfiguration.java     |  31 +++++
 .../main/resources/protocols/plc4x/v0/plc4x.mspec  |  11 ++
 12 files changed, 435 insertions(+), 4 deletions(-)

diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 704970b275..8742246cdd 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -12,12 +12,24 @@ New Features
   notifications.
 - Added a new PlcCertificateAuthentication to the API module.
 - Initial version of a new Java UMAS driver.
+- The 'plc4x' proxy driver now supports TLS as a transport and
+  requires mandatory username/password authentication
+  (configured via the new "username" and "password" connection
+  parameters).
 
 Incompatible changes
 --------------------
 
 - Dropped support for Java 11, new baseline Java version is
   Java 21.
+- The 'plc4x' proxy driver now defaults to the TLS transport
+  instead of plaintext TCP. Existing plaintext connections must
+  switch to an explicit transport prefix (e.g. "plc4x:tcp://...").
+  When using TLS against a server with a self-signed certificate,
+  set "tls.verify-ssl=false" (or pin the certificate).
+- The 'plc4x' proxy driver now requires username/password
+  authentication on connect; connecting without credentials, or
+  with invalid ones, is rejected with an ACCESS_DENIED handshake.
 - The PLC4J-API module however is intentionally held at
   Java 17 to allow alternate driver implementations to support
   Java 17.
@@ -34,6 +46,10 @@ Incompatible changes
 Bug Fixes
 ---------
 
+- Fixed serialization in the 'plc4x' proxy driver's message
+  codec, which did not configure the buffer integer/string
+  encodings under SPI3 and failed to serialize any message.
+
 ==============================================================
 Apache PLC4X 0.13.1
 ==============================================================
diff --git a/plc4j/drivers/plc4x/pom.xml b/plc4j/drivers/plc4x/pom.xml
index 645aa7f014..cd88da10e5 100644
--- a/plc4j/drivers/plc4x/pom.xml
+++ b/plc4j/drivers/plc4x/pom.xml
@@ -130,6 +130,11 @@
       <artifactId>plc4j-transports-tcp</artifactId>
       <version>0.14.0-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-transports-tls</artifactId>
+      <version>0.14.0-SNAPSHOT</version>
+    </dependency>
     <dependency>
       <groupId>org.apache.plc4x</groupId>
       <artifactId>plc4j-utils-audit-log-api</artifactId>
diff --git 
a/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xAuthRequest.java
 
b/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xAuthRequest.java
new file mode 100644
index 0000000000..14c578b093
--- /dev/null
+++ 
b/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xAuthRequest.java
@@ -0,0 +1,149 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.plc4x.java.plc4x.readwrite;
+
+import org.apache.plc4x.java.spi.buffers.api.Message;
+import org.apache.plc4x.java.spi.buffers.api.ReadBuffer;
+import org.apache.plc4x.java.spi.buffers.api.WithOption;
+import org.apache.plc4x.java.spi.buffers.api.WriteBuffer;
+import org.apache.plc4x.java.spi.buffers.api.exceptions.BufferException;
+import org.apache.plc4x.java.spi.buffers.bytebased.WithByteBasedOption;
+import org.apache.plc4x.java.spi.fields.data.reader.DataReaderFactory;
+import org.apache.plc4x.java.spi.fields.data.writer.DataWriterFactory;
+import org.apache.plc4x.java.spi.fields.fields.reader.FieldReaderFactory;
+import org.apache.plc4x.java.spi.fields.fields.writer.FieldWriterFactory;
+import org.apache.plc4x.java.spi.fields.utils.ThreadLocalHelper;
+import org.apache.plc4x.java.spi.utils.StaticHelper;
+
+/**
+ * Code generated by code-generation. DO NOT EDIT.
+ */
+public class Plc4xAuthRequest extends Plc4xMessage implements Message {
+  protected final String username;
+
+  protected final String password;
+
+  public Plc4xAuthRequest(Integer requestId, String username, String password) 
{
+    super(requestId);
+    this.username = username;
+    this.password = password;
+  }
+
+  /**
+   * Discriminator field requestType
+   */
+  @Override
+  public Plc4xRequestType getRequestType() {
+    return Plc4xRequestType.AUTH_REQUEST;
+  }
+
+  /**
+   * Property field username
+   */
+  public String getUsername() {
+    return username;
+  }
+
+  /**
+   * Property field password
+   */
+  public String getPassword() {
+    return password;
+  }
+
+  public static Plc4xMessageBuilder staticParsePlc4xMessageBuilder(ReadBuffer 
readBuffer) throws
+      BufferException {
+    readBuffer.pushContext(WithOption.WithName("Plc4xAuthRequest"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+    int startPos = readBuffer.getPositionInBits();
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    // Implicit Field: usernameLen
+    short usernameLen = 
FieldReaderFactory.readImplicitField(DataReaderFactory.readUnsignedShort(readBuffer,
 8), WithOption.WithName("usernameLen"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+
+    // Simple Field: username
+    String username = 
FieldReaderFactory.readSimpleField(DataReaderFactory.readString(readBuffer, 
(usernameLen) * (8)), WithOption.WithName("username"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+
+    // Implicit Field: passwordLen
+    short passwordLen = 
FieldReaderFactory.readImplicitField(DataReaderFactory.readUnsignedShort(readBuffer,
 8), WithOption.WithName("passwordLen"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+
+    // Simple Field: password
+    String password = 
FieldReaderFactory.readSimpleField(DataReaderFactory.readString(readBuffer, 
(passwordLen) * (8)), WithOption.WithName("password"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+
+    readBuffer.popContext();
+    return new Plc4xMessageBuilderImpl(username, password);
+  }
+
+  protected void serializePlc4xMessageChild(WriteBuffer writeBuffer) throws 
BufferException {
+    writeBuffer.pushContext(WithOption.WithName("Plc4xAuthRequest"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+    int startPos = writeBuffer.getPositionInBits();
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    // Implicit Field: usernameLen
+    short usernameLen = (short) (StaticHelper.STR_LEN(username));
+    FieldWriterFactory.writeImplicitField((short) usernameLen, 
DataWriterFactory.writeUnsignedShort(writeBuffer, 8), 
WithOption.WithName("usernameLen"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+
+    // Simple Field: username
+    FieldWriterFactory.writeSimpleField((String) username, 
DataWriterFactory.writeString(writeBuffer, (usernameLen) * (8)), 
WithOption.WithName("username"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+
+    // Implicit Field: passwordLen
+    short passwordLen = (short) (StaticHelper.STR_LEN(password));
+    FieldWriterFactory.writeImplicitField((short) passwordLen, 
DataWriterFactory.writeUnsignedShort(writeBuffer, 8), 
WithOption.WithName("passwordLen"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+
+    // Simple Field: password
+    FieldWriterFactory.writeSimpleField((String) password, 
DataWriterFactory.writeString(writeBuffer, (passwordLen) * (8)), 
WithOption.WithName("password"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+
+    writeBuffer.popContext();
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = super.getLengthInBits();
+    Plc4xAuthRequest _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    // Implicit Field: usernameLen
+    lengthInBits += 8;
+
+    // Simple Field: username
+    lengthInBits += (StaticHelper.STR_LEN(username)) * (8);
+
+    // Implicit Field: passwordLen
+    lengthInBits += 8;
+
+    // Simple Field: password
+    lengthInBits += (StaticHelper.STR_LEN(password)) * (8);
+
+    return lengthInBits;
+  }
+
+  public static class Plc4xMessageBuilderImpl implements 
Plc4xMessage.Plc4xMessageBuilder {
+    private final String username;
+
+    private final String password;
+
+    public Plc4xMessageBuilderImpl(String username, String password) {
+      this.username = username;
+      this.password = password;
+    }
+
+    public Plc4xMessage build(int requestId) {
+      return new Plc4xAuthRequest(requestId, username, password);
+    }
+  }
+}
diff --git 
a/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xAuthResponse.java
 
b/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xAuthResponse.java
new file mode 100644
index 0000000000..44d2049b31
--- /dev/null
+++ 
b/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xAuthResponse.java
@@ -0,0 +1,106 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.plc4x.java.plc4x.readwrite;
+
+import org.apache.plc4x.java.spi.buffers.api.Message;
+import org.apache.plc4x.java.spi.buffers.api.ReadBuffer;
+import org.apache.plc4x.java.spi.buffers.api.WithOption;
+import org.apache.plc4x.java.spi.buffers.api.WriteBuffer;
+import org.apache.plc4x.java.spi.buffers.api.exceptions.BufferException;
+import org.apache.plc4x.java.spi.buffers.bytebased.WithByteBasedOption;
+import org.apache.plc4x.java.spi.fields.data.reader.DataReaderFactory;
+import org.apache.plc4x.java.spi.fields.data.writer.DataWriterFactory;
+import org.apache.plc4x.java.spi.fields.fields.reader.FieldReaderFactory;
+import org.apache.plc4x.java.spi.fields.fields.writer.FieldWriterFactory;
+import org.apache.plc4x.java.spi.fields.utils.ThreadLocalHelper;
+
+/**
+ * Code generated by code-generation. DO NOT EDIT.
+ */
+public class Plc4xAuthResponse extends Plc4xMessage implements Message {
+  protected final Plc4xResponseCode responseCode;
+
+  public Plc4xAuthResponse(Integer requestId, Plc4xResponseCode responseCode) {
+    super(requestId);
+    this.responseCode = responseCode;
+  }
+
+  /**
+   * Discriminator field requestType
+   */
+  @Override
+  public Plc4xRequestType getRequestType() {
+    return Plc4xRequestType.AUTH_RESPONSE;
+  }
+
+  /**
+   * Property field responseCode
+   */
+  public Plc4xResponseCode getResponseCode() {
+    return responseCode;
+  }
+
+  public static Plc4xMessageBuilder staticParsePlc4xMessageBuilder(ReadBuffer 
readBuffer) throws
+      BufferException {
+    readBuffer.pushContext(WithOption.WithName("Plc4xAuthResponse"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+    int startPos = readBuffer.getPositionInBits();
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    // Simple Field (enum): responseCode
+    Plc4xResponseCode responseCode = 
FieldReaderFactory.readEnumField(DataReaderFactory.readEnum(Plc4xResponseCode::enumForValue,
 DataReaderFactory.readUnsignedShort(readBuffer, 8)), 
WithOption.WithName("responseCode"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+
+    readBuffer.popContext();
+    return new Plc4xMessageBuilderImpl(responseCode);
+  }
+
+  protected void serializePlc4xMessageChild(WriteBuffer writeBuffer) throws 
BufferException {
+    writeBuffer.pushContext(WithOption.WithName("Plc4xAuthResponse"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+    int startPos = writeBuffer.getPositionInBits();
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    // Simple Field (enum): responseCode
+    FieldWriterFactory.writeSimpleEnumField((Plc4xResponseCode) responseCode, 
DataWriterFactory.writeEnum(Plc4xResponseCode::getValue, 
Plc4xResponseCode::name, DataWriterFactory.writeUnsignedShort(writeBuffer, 8)), 
WithOption.WithName("responseCode"), 
WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+
+    writeBuffer.popContext();
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = super.getLengthInBits();
+    Plc4xAuthResponse _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    // Simple Field: responseCode
+    lengthInBits += 8;
+
+    return lengthInBits;
+  }
+
+  public static class Plc4xMessageBuilderImpl implements 
Plc4xMessage.Plc4xMessageBuilder {
+    private final Plc4xResponseCode responseCode;
+
+    public Plc4xMessageBuilderImpl(Plc4xResponseCode responseCode) {
+      this.responseCode = responseCode;
+    }
+
+    public Plc4xMessage build(int requestId) {
+      return new Plc4xAuthResponse(requestId, responseCode);
+    }
+  }
+}
diff --git 
a/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xMessage.java
 
b/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xMessage.java
index c83f078ac2..8318fcb663 100644
--- 
a/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xMessage.java
+++ 
b/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xMessage.java
@@ -81,7 +81,11 @@ public abstract class Plc4xMessage implements Message {
 
     // Switch Field
     Plc4xMessageBuilder builder = null;
-    if (EvaluationHelper.equals(requestType, (Plc4xRequestType) 
(Plc4xRequestType.CONNECT_REQUEST))) {
+    if (EvaluationHelper.equals(requestType, (Plc4xRequestType) 
(Plc4xRequestType.AUTH_REQUEST))) {
+      builder = Plc4xAuthRequest.staticParsePlc4xMessageBuilder(readBuffer);
+    } else if (EvaluationHelper.equals(requestType, (Plc4xRequestType) 
(Plc4xRequestType.AUTH_RESPONSE))) {
+      builder = Plc4xAuthResponse.staticParsePlc4xMessageBuilder(readBuffer);
+    } else if (EvaluationHelper.equals(requestType, (Plc4xRequestType) 
(Plc4xRequestType.CONNECT_REQUEST))) {
       builder = Plc4xConnectRequest.staticParsePlc4xMessageBuilder(readBuffer);
     } else if (EvaluationHelper.equals(requestType, (Plc4xRequestType) 
(Plc4xRequestType.CONNECT_RESPONSE))) {
       builder = 
Plc4xConnectResponse.staticParsePlc4xMessageBuilder(readBuffer);
diff --git 
a/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xRequestType.java
 
b/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xRequestType.java
index a19acef92b..8945109cb4 100644
--- 
a/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xRequestType.java
+++ 
b/plc4j/drivers/plc4x/src/main/generated/org/apache/plc4x/java/plc4x/readwrite/Plc4xRequestType.java
@@ -45,7 +45,11 @@ public enum Plc4xRequestType {
 
   UNSUBSCRIPTION_REQUEST((short) 0x0B),
 
-  UNSUBSCRIPTION_RESPONSE((short) 0x0C);
+  UNSUBSCRIPTION_RESPONSE((short) 0x0C),
+
+  AUTH_REQUEST((short) 0x0D),
+
+  AUTH_RESPONSE((short) 0x0E);
 
   private static final Map<Short, Plc4xRequestType> map;
 
diff --git 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xConnection.java
 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xConnection.java
index 089b88cb04..597cb0b51d 100644
--- 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xConnection.java
+++ 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xConnection.java
@@ -27,6 +27,8 @@ import org.apache.plc4x.java.api.messages.PlcWriteResponse;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
 import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.plc4x.config.Plc4xConfiguration;
+import org.apache.plc4x.java.plc4x.readwrite.Plc4xAuthRequest;
+import org.apache.plc4x.java.plc4x.readwrite.Plc4xAuthResponse;
 import org.apache.plc4x.java.plc4x.readwrite.Plc4xConnectRequest;
 import org.apache.plc4x.java.plc4x.readwrite.Plc4xConnectResponse;
 import org.apache.plc4x.java.plc4x.readwrite.Plc4xMessage;
@@ -121,6 +123,10 @@ public class Plc4xConnection extends 
ConnectionBase<Plc4xConfiguration> {
             }
         });
 
+        // Authenticate first. The proxy mandates username/password auth; no 
operation is
+        // permitted until this exchange succeeds. We never log the 
credentials.
+        authenticate();
+
         // Open the underlying proxied connection.
         int requestId = txIdGenerator.getAndIncrement();
         CompletableFuture<Plc4xMessage> future = registerPending(requestId);
@@ -147,6 +153,45 @@ public class Plc4xConnection extends 
ConnectionBase<Plc4xConfiguration> {
         }
     }
 
+    /**
+     * Performs the mandatory username/password handshake with the proxy. 
Throws if the
+     * server rejects the credentials or the exchange does not complete in 
time. Credentials
+     * are taken from the connection configuration and are never logged.
+     */
+    private void authenticate() throws PlcConnectionException {
+        int requestId = txIdGenerator.getAndIncrement();
+        CompletableFuture<Plc4xMessage> future = registerPending(requestId);
+        String username = configuration.getUsername();
+        String password = configuration.getPassword();
+        if (username == null || password == null) {
+            pendingResponses.remove(requestId);
+            throw new PlcConnectionException(
+                "Username and password are required to connect to a PLC4X 
proxy server");
+        }
+        try {
+            messageCodec.send(new Plc4xAuthRequest(requestId, username, 
password));
+        } catch (MessageCodecException e) {
+            pendingResponses.remove(requestId);
+            throw new PlcConnectionException("Failed to send proxy 
authentication request", e);
+        }
+        try {
+            Plc4xMessage response = future
+                .orTimeout(configuration.getRequestTimeout(), 
TimeUnit.MILLISECONDS)
+                .get();
+            if (!(response instanceof Plc4xAuthResponse authResponse)) {
+                throw new PlcConnectionException("Unexpected response to proxy 
authentication: " + response);
+            }
+            if (authResponse.getResponseCode() != Plc4xResponseCode.OK) {
+                throw new PlcConnectionException(
+                    "Authentication against PLC4X proxy server failed: " + 
authResponse.getResponseCode());
+            }
+        } catch (PlcConnectionException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new PlcConnectionException("Error during proxy 
authentication", e);
+        }
+    }
+
     @Override
     public void close() throws Exception {
         handshakeComplete = false;
diff --git 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xDriver.java
 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xDriver.java
index 0a0c7b7d3b..9ad492fbfc 100644
--- 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xDriver.java
+++ 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xDriver.java
@@ -20,6 +20,7 @@ package org.apache.plc4x.java.plc4x;
 
 import org.apache.plc4x.java.plc4x.config.Plc4xConfiguration;
 import org.apache.plc4x.java.plc4x.config.Plc4xTcpTransportConfiguration;
+import org.apache.plc4x.java.plc4x.config.Plc4xTlsTransportConfiguration;
 import org.apache.plc4x.java.spi.config.Configuration;
 import org.apache.plc4x.java.spi.drivers.ConnectionBase;
 import org.apache.plc4x.java.spi.drivers.DriverBase;
@@ -53,17 +54,23 @@ public class Plc4xDriver extends DriverBase {
         if ("tcp".equals(transport.getTransportCode())) {
             return Plc4xTcpTransportConfiguration.class;
         }
+        if ("tls".equals(transport.getTransportCode())) {
+            return Plc4xTlsTransportConfiguration.class;
+        }
         return super.getTransportConfigurationClass(transport);
     }
 
     @Override
     public Optional<String> getDefaultTransportCode() {
-        return Optional.of("tcp");
+        // TLS is the default transport - the proxy carries credentials, so 
the channel should
+        // be encrypted by default. Plaintext TCP remains available as an 
explicit opt-in
+        // (e.g. "plc4x:tcp://...") for trusted networks or testing.
+        return Optional.of("tls");
     }
 
     @Override
     public List<String> getSupportedTransportCodes() {
-        return List.of("tcp", "test");
+        return List.of("tls", "tcp", "test");
     }
 
     @Override
diff --git 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xMessageCodec.java
 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xMessageCodec.java
index a28283954a..19283f8038 100644
--- 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xMessageCodec.java
+++ 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xMessageCodec.java
@@ -19,8 +19,11 @@
 package org.apache.plc4x.java.plc4x;
 
 import org.apache.plc4x.java.plc4x.readwrite.Plc4xMessage;
+import org.apache.plc4x.java.spi.buffers.api.WithOption;
 import org.apache.plc4x.java.spi.buffers.api.exceptions.BufferException;
 import org.apache.plc4x.java.spi.buffers.bytebased.ReadBufferByteBased;
+import org.apache.plc4x.java.spi.buffers.bytebased.WithByteBasedOption;
+import org.apache.plc4x.java.spi.buffers.bytebased.WriteBufferByteBased;
 import org.apache.plc4x.java.spi.drivers.MessageCodecBase;
 import org.apache.plc4x.java.spi.drivers.exceptions.MessageCodecException;
 import org.apache.plc4x.java.spi.transports.api.TransportInstance;
@@ -67,4 +70,30 @@ public class Plc4xMessageCodec extends 
MessageCodecBase<Plc4xMessage> {
         return Plc4xMessage.staticParse(readBuffer);
     }
 
+    /**
+     * The generated serializers/parsers ask the buffer for an unsigned-binary 
integer encoding
+     * (and signed/float encodings) for fields that don't pass an explicit 
option. The default
+     * {@link MessageCodecBase} buffers do not pre-configure those, which 
trips field writers like
+     * {@code writeUnsignedShort}, so we supply the big-endian defaults the 
protocol expects.
+     */
+    @Override
+    protected WriteBufferByteBased createWriteBuffer(int size) {
+        return new WriteBufferByteBased(new byte[size],
+            WithOption.WithUnsignedIntegerEncoding("unsigned-binary"),
+            WithOption.WithSignedIntegerEncoding("twos-complement"),
+            WithOption.WithFloatEncoding("IEEE754"),
+            WithOption.WithStringEncoding("UTF8"),
+            WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+    }
+
+    @Override
+    protected ReadBufferByteBased createReadBuffer(byte[] data) {
+        return new ReadBufferByteBased(data,
+            WithOption.WithUnsignedIntegerEncoding("unsigned-binary"),
+            WithOption.WithSignedIntegerEncoding("twos-complement"),
+            WithOption.WithFloatEncoding("IEEE754"),
+            WithOption.WithStringEncoding("UTF8"),
+            WithByteBasedOption.WithByteOrder("BIG_ENDIAN"));
+    }
+
 }
diff --git 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/config/Plc4xConfiguration.java
 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/config/Plc4xConfiguration.java
index 6a9feb0ae7..115bd78d77 100644
--- 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/config/Plc4xConfiguration.java
+++ 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/config/Plc4xConfiguration.java
@@ -34,6 +34,14 @@ public class Plc4xConfiguration implements Configuration {
     @Description("Default timeout for all types of requests.")
     private int requestTimeout;
 
+    @ConfigurationParameter("username")
+    @Description("Username for authenticating against the PLC4X proxy server. 
Authentication is mandatory.")
+    private String username;
+
+    @ConfigurationParameter("password")
+    @Description("Password for authenticating against the PLC4X proxy server. 
Authentication is mandatory.")
+    private String password;
+
     public String getRemoteConnectionString() {
         return remoteConnectionString;
     }
@@ -42,6 +50,22 @@ public class Plc4xConfiguration implements Configuration {
         this.remoteConnectionString = remoteConnectionString;
     }
 
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
     public int getRequestTimeout() {
         return requestTimeout;
     }
diff --git 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/config/Plc4xTlsTransportConfiguration.java
 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/config/Plc4xTlsTransportConfiguration.java
new file mode 100644
index 0000000000..1545ee950c
--- /dev/null
+++ 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/config/Plc4xTlsTransportConfiguration.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.plc4x.java.plc4x.config;
+
+import org.apache.plc4x.java.plc4x.readwrite.Constants;
+import org.apache.plc4x.java.transport.tls.config.TlsTransportConfiguration;
+
+public class Plc4xTlsTransportConfiguration extends TlsTransportConfiguration {
+
+    @Override
+    public int getDefaultPort() {
+        return Constants.PLC4XTCPDEFAULTPORT;
+    }
+
+}
diff --git a/protocols/plc4x/src/main/resources/protocols/plc4x/v0/plc4x.mspec 
b/protocols/plc4x/src/main/resources/protocols/plc4x/v0/plc4x.mspec
index 3d88d6f6ad..ad75f472b4 100644
--- a/protocols/plc4x/src/main/resources/protocols/plc4x/v0/plc4x.mspec
+++ b/protocols/plc4x/src/main/resources/protocols/plc4x/v0/plc4x.mspec
@@ -27,6 +27,15 @@
     [simple        uint 16          requestId                   ]
     [discriminator Plc4xRequestType requestType                 ]
     [typeSwitch requestType
+        ['AUTH_REQUEST' Plc4xAuthRequest
+            [implicit uint 8                      usernameLen 
'STR_LEN(username)']
+            [simple   vstring 'usernameLen * 8'   username                     
  ]
+            [implicit uint 8                      passwordLen 
'STR_LEN(password)']
+            [simple   vstring 'passwordLen * 8'   password                     
  ]
+        ]
+        ['AUTH_RESPONSE' Plc4xAuthResponse
+            [simple   Plc4xResponseCode           responseCode                 
  ]
+        ]
         ['CONNECT_REQUEST' Plc4xConnectRequest
             [implicit uint 8                            connectionStringLen 
'STR_LEN(connectionString)']
             [simple   vstring 'connectionStringLen * 8' connectionString       
                        ]
@@ -229,6 +238,8 @@
     ['0x0A' SUBSCRIPTION_RESPONSE  ]
     ['0x0B' UNSUBSCRIPTION_REQUEST ]
     ['0x0C' UNSUBSCRIPTION_RESPONSE]
+    ['0x0D' AUTH_REQUEST           ]
+    ['0x0E' AUTH_RESPONSE          ]
 ]
 
 [enum uint 8 Plc4xResponseCode

Reply via email to