You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by hu...@apache.org on 2021/02/09 12:43:43 UTC

[plc4x] 02/02: [BROKEN] Asymmetric Encryption works.

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

hutcheb pushed a commit to branch feature/native_opua_client
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit fd821cde2e6e534a70750e679a9fd303b046e8f4
Author: hutcheb <be...@gmail.com>
AuthorDate: Tue Feb 9 07:42:09 2021 -0500

    [BROKEN] Asymmetric Encryption works.
    
    Verifying the asymmetic signature doesn't
    Haven't implemented symmetric encryption.
    
    Started to refactor a little bit.
---
 .../java/opcua/config/OpcuaConfiguration.java      |   2 +-
 .../java/opcua/context/EncryptionHandler.java      | 113 +++++---
 .../java/opcua/protocol/OpcuaProtocolLogic.java    | 313 +++++++--------------
 .../src/main/resources/protocols/opcua/opcua.mspec |  24 +-
 protocols/opcua/src/main/xslt/opc-types.xsl        |   5 +-
 5 files changed, 200 insertions(+), 257 deletions(-)

diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/config/OpcuaConfiguration.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/config/OpcuaConfiguration.java
index 8b6d53f..d8d1aee 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/config/OpcuaConfiguration.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/config/OpcuaConfiguration.java
@@ -85,7 +85,7 @@ public class OpcuaConfiguration implements Configuration, TcpTransportConfigurat
     @ConfigurationParameter("certDirectory")
     private String certDirectory;
 
-    @ConfigurationParameter("keyStorePassword")
+        @ConfigurationParameter("keyStorePassword")
     private String keyStorePassword;
 
     private CertificateKeyPair ckp;
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/EncryptionHandler.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/EncryptionHandler.java
index e9b3d82..270c3fd 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/EncryptionHandler.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/EncryptionHandler.java
@@ -19,6 +19,7 @@ under the License.
 
 package org.apache.plc4x.java.opcua.context;
 
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.opcua.protocol.OpcuaProtocolLogic;
 import org.apache.plc4x.java.opcua.readwrite.MessagePDU;
 import org.apache.plc4x.java.opcua.readwrite.OpcuaAPU;
@@ -27,14 +28,15 @@ import org.apache.plc4x.java.opcua.readwrite.io.OpcuaAPUIO;
 import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
 import java.io.ByteArrayInputStream;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
+import java.security.*;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 
@@ -42,6 +44,11 @@ public class EncryptionHandler {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(OpcuaProtocolLogic.class);
 
+    static {
+        // Required for SecurityPolicy.Aes256_Sha256_RsaPss
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
     private X509Certificate serverCertificate;
     private X509Certificate clientCertificate;
     private PrivateKey clientPrivateKey;
@@ -54,47 +61,72 @@ public class EncryptionHandler {
         this.serverCertificate = getCertificateX509(senderCertificate);
     }
 
-    public ReadBuffer decodeOpcuaOpenResponse(OpcuaOpenResponse message) throws ParseException {
-        int encryptedLength = message.getLengthInBytes();
-        LOGGER.info("Encrypted Length {}", encryptedLength);
-        int encryptedMessageLength = message.getMessage().length + 8;
-        LOGGER.info("Encrypted Message Length {}", encryptedMessageLength);
+    public ReadBuffer encodeMessage(MessagePDU pdu, byte[] message) {
+        int PREENCRYPTED_BLOCK_LENGTH = 190;
+        int unencryptedLength = pdu.getLengthInBytes();
+        int openRequestLength = message.length;
+        int positionFirstBlock = unencryptedLength - openRequestLength - 8;
+        int paddingSize = PREENCRYPTED_BLOCK_LENGTH - ((openRequestLength + 256 + 1 + 8) % PREENCRYPTED_BLOCK_LENGTH);
+        int preEncryptedLength = openRequestLength + 256 + 1 + 8 + paddingSize;
+        if (preEncryptedLength % PREENCRYPTED_BLOCK_LENGTH != 0) {
+            throw new PlcRuntimeException("Pre encrypted block length " + preEncryptedLength + " isn't a multiple of the block size");
+        }
+        int numberOfBlocks = preEncryptedLength / PREENCRYPTED_BLOCK_LENGTH;
+        int encryptedLength = numberOfBlocks * 256 + positionFirstBlock;
+        WriteBuffer buf = new WriteBuffer(encryptedLength, true);
+        try {
+            OpcuaAPUIO.staticSerialize(buf, new OpcuaAPU(pdu));
+            byte paddingByte = (byte) paddingSize;
+            buf.writeByte(8, paddingByte);
+            for (int i = 0; i < paddingSize; i++) {
+                buf.writeByte(8, paddingByte);
+            }
+            //Writing Message Length
+            int tempPos = buf.getPos();
+            buf.setPos(4);
+            buf.writeInt(32, encryptedLength);
+            buf.setPos(tempPos);
+            byte[] signature = sign(buf.getBytes(0, unencryptedLength + paddingSize + 1));
+            //Write the signature to the end of the buffer
+            for (int i = 0; i < signature.length; i++) {
+                buf.writeByte(8, signature[i]);
+            }
+            buf.setPos(positionFirstBlock);
+            encryptBlock(buf, buf.getBytes(positionFirstBlock, positionFirstBlock + preEncryptedLength));
+            return new ReadBuffer(buf.getData(), true);
+        } catch (ParseException e) {
+            throw new PlcRuntimeException("Unable to parse apu prior to encrypting");
+        }
+    }
+
+    public ReadBuffer decodeMessage(MessagePDU pdu, byte[] message) throws ParseException {
+        int encryptedLength = pdu.getLengthInBytes();
+        int encryptedMessageLength = message.length + 8;
         int headerLength = encryptedLength - encryptedMessageLength;
-        LOGGER.info("Header Length {}", headerLength);
         int numberOfBlocks = encryptedMessageLength / 256;
-        LOGGER.info("Number of Blocks Length {}", numberOfBlocks);
         WriteBuffer buf = new WriteBuffer(headerLength + numberOfBlocks * 256,true);
-        OpcuaAPUIO.staticSerialize(buf, new OpcuaAPU(message));
+        OpcuaAPUIO.staticSerialize(buf, new OpcuaAPU(pdu));
         byte[] data = buf.getBytes(headerLength, encryptedLength);
         buf.setPos(headerLength);
         decryptBlock(buf, data);
         int tempPos = buf.getPos();
-        LOGGER.info("Position at end of Decryption:- {}", tempPos);
-        LOGGER.info("Position at end of Decryption within Messgae:- {}", tempPos - headerLength);
-        buf.setPos(4);
-        buf.writeInt(32, tempPos - 208 - 1);
-        //Check Signature, etc..
         buf.setPos(0);
-        byte[] unencrypted = buf.getBytes(0, tempPos - 208);
-        byte[] signature = buf.getBytes(tempPos - 208, tempPos);
-        LOGGER.info("Signature Length:- {}" + signature.length);
-        LOGGER.info("Signature;- {}", signature);
-        if (!checkSignature(unencrypted, signature)) {
-            LOGGER.info("Signature verification failed: - {}", unencrypted);
+        if (!checkSignature(buf.getBytes(0, tempPos))) {
+            LOGGER.info("Signature verification failed: - {}", buf.getBytes(0, tempPos - 256));
         }
-        ReadBuffer readBuffer = new ReadBuffer(unencrypted, true);
-        OpcuaOpenResponse opcuaOpenResponse = (OpcuaOpenResponse) OpcuaAPUIO.staticParse(readBuffer, true).getMessage();
-        return new ReadBuffer(opcuaOpenResponse.getMessage(), true);
+        buf.setPos(4);
+        buf.writeInt(32, tempPos - 256);
+        return new ReadBuffer(buf.getBytes(0, tempPos - 256), true);
     }
 
     public void decryptBlock(WriteBuffer buf, byte[] data) {
         try {
             Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
             cipher.init(Cipher.DECRYPT_MODE, this.clientPrivateKey);
+
             for (int i = 0; i < data.length; i += 256) {
-                LOGGER.info("Decrypt Iterate:- {}, Data Length:- {}", i, data.length);
                 byte[] decrypted = cipher.doFinal(data, i, 256);
-                for (int j = 0; j < 190; j++) {
+                for (int j = 0; j < 214; j++) {
                     buf.writeByte(8, decrypted[j]);
                 }
             }
@@ -104,18 +136,20 @@ public class EncryptionHandler {
         }
     }
 
-    public boolean checkSignature(byte[] data, byte[] senderSignature) {
+    public boolean checkSignature(byte[] data) {
         try {
             LOGGER.info("Server public key size {}", this.serverCertificate.getPublicKey().getEncoded().length);
             LOGGER.info("Server public key format {}", this.serverCertificate.getPublicKey().getFormat());
             LOGGER.info("Server public key {}", this.serverCertificate.getPublicKey().toString());
-            LOGGER.info("Server public key Algo{}", this.serverCertificate.getPublicKey().getAlgorithm());
+            LOGGER.info("Server public key Algo -{}", this.serverCertificate.getPublicKey().getAlgorithm());
+            LOGGER.info("Server public key Algo Name -{}",this.serverCertificate.getSigAlgName());
+            LOGGER.info("Providers:- {}", Security.getProviders());
+
 
-            //Signature signature = Signature.getInstance("HMAC-SHA-256", "BC");
             Signature signature = Signature.getInstance("SHA256withRSA", "BC");
-            signature.initVerify(this.serverCertificate.getPublicKey());
+            signature.initVerify(serverCertificate.getPublicKey());
             signature.update(data);
-            return signature.verify(senderSignature);
+            return signature.verify(data, 0, data.length - 256);
 
         } catch (Exception e) {
             e.printStackTrace();
@@ -153,6 +187,21 @@ public class EncryptionHandler {
         }
     }
 
+    public void encryptHmacBlock(WriteBuffer buf, byte[] data) {
+        try {
+            Mac cipher = Mac.getInstance("HmacSHA256");
+            SecretKeySpec keySpec = new SecretKeySpec(getSecretKey(), "HmacSHA256");
+            cipher.init(keySpec);
+        } catch (Exception e) {
+            LOGGER.error("Unable to encrypt Data");
+            e.printStackTrace();
+        }
+    }
+
+    public byte[] getSecretKey() {
+        return null;
+    }
+
 
 
     public X509Certificate getCertificateX509(byte[] senderCertificate) {
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
index 1acf172..e54ee85 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
@@ -49,42 +49,21 @@ import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
 import org.apache.plc4x.java.spi.values.PlcList;
-import org.bouncycastle.asn1.pkcs.RSAPublicKey;
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.BufferedAsymmetricBlockCipher;
-import org.bouncycastle.crypto.engines.RSAEngine;
-import org.bouncycastle.crypto.macs.HMac;
-import org.bouncycastle.crypto.modes.CBCBlockCipher;
-import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
-import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.crypto.params.RSAKeyParameters;
-import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey;
-import org.bouncycastle.jce.provider.X509CertificateObject;
-import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.crypto.Cipher;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.security.Signature;
 import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -102,33 +81,33 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     private static final int DEFAULT_SEND_BUFFER_SIZE = 65535;
     private static final int VERSION = 0;
     private static final String PASSWORD_ENCRYPTION_ALGORITHM = "http://www.w3.org/2001/04/xmlenc#rsa-oaep";
-
-    private NodeId authenticationToken = new NodeIdTwoByte(NodeIdType.nodeIdTypeTwoByte, new TwoByteNodeId((short) 0));
-    private static final PascalString NULL_STRING = new PascalString(-1,null);
-    private static final PascalByteString NULL_BYTE_STRING = new PascalByteString(-1, new byte[0]);
+    private static final PascalString NULL_STRING = new PascalString(null);
+    private static final PascalByteString NULL_BYTE_STRING = new PascalByteString( new byte[0]);
     private static ExpandedNodeId NULL_EXPANDED_NODEID = new ExpandedNodeIdTwoByte(false,
-                                                                                    false,
-                                                                                    null,
-                                                                                    null,
-                                                                                    new TwoByteNodeId((short) 0));
+        false,
+        null,
+        null,
+        new TwoByteNodeId((short) 0));
     private static final ExtensionObject NULL_EXTENSION_OBJECT = new ExtensionObject(NULL_EXPANDED_NODEID,
-                                                                                        (short) 0,
-                                                                                null,               //Body Length
-                                                                                    null);               // Body
-    private static final long epochOffset = 116444736000000000L;         //Offset between OPC UA epoch time and linux epoch time.
+        (short) 0,
+        null,               //Body Length
+        null);               // Body
+    private static final long EPOCH_OFFSET = 116444736000000000L;         //Offset between OPC UA epoch time and linux epoch time.
+    private static final String APPLICATION_URI = new PascalString("urn:apache:plc4x:client");
+    private static final String PRODUCT_URI = new PascalString("urn:apache:plc4x:client");
+    private static final PascalString APPLICATION_TEXT = new PascalString("OPCUA client for the Apache PLC4X:PLC4J project");
+    private static final String FINAL_CHUNK = "F";
+    private static final String CONTINUATION_CHUNK = "C";
+    private static final String ABORT_CHUNK = "F";
 
-    private static final String CHUNK = "F";
-
-    private static final String applicationUri = "urn:apache:plc4x:client";
-    private static final String productUri = "urn:apache:plc4x:client";
-    private static final String applicationText = "OPCUA client for the Apache PLC4X:PLC4J project";
+    private NodeId authenticationToken = new NodeIdTwoByte(NodeIdType.nodeIdTypeTwoByte, new TwoByteNodeId((short) 0));
 
-    private final String sessionName = "UaSession:" + applicationText + ":" + RandomStringUtils.random(20, true, true);
+    private final PascalString sessionName = new PascalString("UaSession:" + APPLICATION_TEXT + ":" + RandomStringUtils.random(20, true, true));
     private final byte[] clientNonce = RandomUtils.nextBytes(40);
-    private RequestTransactionManager tm;
+    private RequestTransactionManager tm = new RequestTransactionManager(1);
 
-    private PascalString policyId = null;
-    private String endpoint;
+    private PascalString policyId;
+    private PascalString endpoint;
     private boolean discovery;
     private String username;
     private String password;
@@ -146,7 +125,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     private AtomicInteger channelId = new AtomicInteger(1);
     private byte[] senderCertificate = null;
     private byte[] senderNonce = null;
-    private String certificateThumbprint = null;
+    private PascalByteString certificateThumbprint = null;
     private OpcuaConfiguration configuration;
     private EncryptionHandler encryptionHandler = null;
 
@@ -155,35 +134,31 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     @Override
     public void setConfiguration(OpcuaConfiguration configuration) {
         this.configuration = configuration;
-        this.endpoint = configuration.getEndpoint();
+        this.endpoint = new PascalString(configuration.getEndpoint());
         this.discovery = configuration.isDiscovery();
         this.username = configuration.getUsername();
         this.password = configuration.getPassword();
         this.certFile = configuration.getCertDirectory();
         this.securityPolicy = "http://opcfoundation.org/UA/SecurityPolicy#" + configuration.getSecurityPolicy();
-        LOGGER.info("---------------------------------------");
-        LOGGER.info(configuration.getSecurityPolicy());
         this.ckp = configuration.getCertificateKeyPair();
 
         if (configuration.getSecurityPolicy().equals("Basic256Sha256")) {
-            this.encryptionHandler = new EncryptionHandler(this.ckp, configuration.getSenderCertificate());
+            //Sender Certificate gets populated during the discover phase when encryption is enabled.
+            this.senderCertificate = configuration.getSenderCertificate();
+            this.encryptionHandler = new EncryptionHandler(this.ckp, this.senderCertificate);
             try {
                 this.publicCertificate = new PascalByteString(this.ckp.getCertificate().getEncoded().length, this.ckp.getCertificate().getEncoded());
                 this.isEncrypted = true;
             } catch (CertificateEncodingException e) {
-                LOGGER.error("Failed to encode the certificate");
+                throw new PlcRuntimeException("Failed to encode the certificate");
             }
-            //this.thumbprint = new PascalByteString(this.ckp.getThumbPrint().length, this.ckp.getThumbPrint());
             this.thumbprint = configuration.getThumbprint();
-            this.senderCertificate = configuration.getSenderCertificate();
         } else {
             this.publicCertificate = NULL_BYTE_STRING;
             this.thumbprint = NULL_BYTE_STRING;
             this.isEncrypted = false;
         }
         this.keyStoreFile = configuration.getKeyStoreFile();
-
-        this.tm = new RequestTransactionManager(1);
     }
 
     @Override
@@ -193,9 +168,9 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
 
     @Override
     public void onDisconnect(ConversationContext<OpcuaAPU> context) {
-        int transactionId = getTransactionIdentifier(securedConnection.get());
+        int transactionId = getTransactionIdentifier();
 
-        int requestHandle = getRequestHandle(securedConnection.get());
+        int requestHandle = getRequestHandle();
 
         ExpandedNodeId expandedNodeId = new ExpandedNodeIdFourByte(false,           //Namespace Uri Specified
             false,            //Server Index Specified
@@ -220,7 +195,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             WriteBuffer buffer = new WriteBuffer(closeSessionRequest.getLengthInBytes(), true);
             OpcuaMessageIO.staticSerialize(buffer, closeSessionRequest);
 
-            OpcuaMessageRequest messageRequest = new OpcuaMessageRequest(CHUNK,
+            OpcuaMessageRequest messageRequest = new OpcuaMessageRequest(FINAL_CHUNK,
                 channelId.get(),
                 tokenId.get(),
                 transactionId,
@@ -242,7 +217,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
 
     private void onDisconnectCloseSecureChannel(ConversationContext<OpcuaAPU> context) {
 
-        int transactionId = getTransactionIdentifier(securedConnection.get());
+        int transactionId = getTransactionIdentifier();
 
         ExpandedNodeId expandedNodeId = new ExpandedNodeIdFourByte(false,           //Namespace Uri Specified
             false,            //Server Index Specified
@@ -262,7 +237,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             (byte) 0,
             requestHeader);
 
-        OpcuaCloseRequest closeRequest = new OpcuaCloseRequest(CHUNK,
+        OpcuaCloseRequest closeRequest = new OpcuaCloseRequest(FINAL_CHUNK,
             channelId.get(),
             tokenId.get(),
             transactionId,
@@ -284,10 +259,6 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         super.setDriverContext(driverContext);
 
         // Initialize Transaction Manager.
-        // Until the number of concurrent requests is successfully negotiated we set it to a
-        // maximum of only one request being able to be sent at a time. During the login process
-        // No concurrent requests can be sent anyway. It will be updated when receiving the
-        // S7ParameterSetupCommunication response.
         this.tm = new RequestTransactionManager(1);
     }
 
@@ -296,13 +267,12 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         // Only the TCP transport supports login.
         LOGGER.info("Opcua Driver running in ACTIVE mode.");
 
-        OpcuaHelloRequest hello = new OpcuaHelloRequest(CHUNK,
+        OpcuaHelloRequest hello = new OpcuaHelloRequest(FINAL_CHUNK,
             VERSION,
             DEFAULT_RECEIVE_BUFFER_SIZE,
             DEFAULT_SEND_BUFFER_SIZE,
             DEFAULT_MAX_MESSAGE_SIZE,
             DEFAULT_MAX_CHUNK_COUNT,
-            this.endpoint.length(),
             this.endpoint);
 
         context.sendRequest(new OpcuaAPU(hello))
@@ -320,13 +290,12 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         // Only the TCP transport supports login.
         LOGGER.info("Opcua Driver running in ACTIVE mode, discovering endpoints");
 
-        OpcuaHelloRequest hello = new OpcuaHelloRequest(CHUNK,
+        OpcuaHelloRequest hello = new OpcuaHelloRequest(FINAL_CHUNK,
             VERSION,
             DEFAULT_RECEIVE_BUFFER_SIZE,
             DEFAULT_SEND_BUFFER_SIZE,
             DEFAULT_MAX_MESSAGE_SIZE,
             DEFAULT_MAX_CHUNK_COUNT,
-            this.endpoint.length(),
             this.endpoint);
 
         context.sendRequest(new OpcuaAPU(hello))
@@ -340,7 +309,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     }
 
     public void onDiscoverOpenSecureChannel(ConversationContext<OpcuaAPU> context, OpcuaAcknowledgeResponse opcuaAcknowledgeResponse) {
-        int transactionId = getTransactionIdentifier(securedConnection.get());
+        int transactionId = getTransactionIdentifier();
 
         ExpandedNodeId expandedNodeId = new ExpandedNodeIdFourByte(false,           //Namespace Uri Specified
             false,            //Server Index Specified
@@ -369,9 +338,9 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             WriteBuffer buffer = new WriteBuffer(openSecureChannelRequest.getLengthInBytes(), true);
             OpcuaMessageIO.staticSerialize(buffer, openSecureChannelRequest);
 
-            OpcuaOpenRequest openRequest = new OpcuaOpenRequest(CHUNK,
+            OpcuaOpenRequest openRequest = new OpcuaOpenRequest(FINAL_CHUNK,
                 0,
-                new PascalString("http://opcfoundation.org/UA/SecurityPolicy#None".length(), "http://opcfoundation.org/UA/SecurityPolicy#None"),
+                new PascalString("http://opcfoundation.org/UA/SecurityPolicy#None"),
                 NULL_BYTE_STRING,
                 NULL_BYTE_STRING,
                 transactionId,
@@ -410,7 +379,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         tokenId.set((int) openSecureChannelResponse.getSecurityToken().getTokenId());
         channelId.set((int) openSecureChannelResponse.getSecurityToken().getChannelId());
 
-        int transactionId = getTransactionIdentifier(securedConnection.get());
+        int transactionId = getTransactionIdentifier();
 
         Integer nextSequenceNumber = opcuaOpenResponse.getSequenceNumber() + 1;
         Integer nextRequestId = opcuaOpenResponse.getRequestId() + 1;
@@ -431,7 +400,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         GetEndpointsRequest endpointsRequest = new GetEndpointsRequest((byte) 1,
             (byte) 0,
             requestHeader,
-            new PascalString(this.endpoint.length(), this.endpoint),
+            this.endpoint,
             0,
             null,
             0,
@@ -441,7 +410,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             WriteBuffer buffer = new WriteBuffer(endpointsRequest.getLengthInBytes(), true);
             OpcuaMessageIO.staticSerialize(buffer, endpointsRequest);
 
-            OpcuaMessageRequest messageRequest = new OpcuaMessageRequest(CHUNK,
+            OpcuaMessageRequest messageRequest = new OpcuaMessageRequest(FINAL_CHUNK,
                 channelId.get(),
                 tokenId.get(),
                 nextSequenceNumber,
@@ -466,8 +435,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                             for (EndpointDescription endpoint : endpoints) {
                                 LOGGER.info(endpoint.getEndpointUrl().getStringValue());
                                 LOGGER.info(endpoint.getSecurityPolicyUri().getStringValue());
-                                if (endpoint.getEndpointUrl().getStringValue().equals(this.endpoint) && endpoint.getSecurityPolicyUri().getStringValue().equals(this.securityPolicy)) {
-                                    LOGGER.info("Found OPC UA endpoint {}", this.endpoint);
+                                if (endpoint.getEndpointUrl().getStringValue().equals(this.endpoint.getStringValue()) && endpoint.getSecurityPolicyUri().getStringValue().equals(this.securityPolicy)) {
+                                    LOGGER.info("Found OPC UA endpoint {}", this.endpoint.getStringValue());
                                     this.configuration.setSenderCertificate(endpoint.getServerCertificate().getStringValue());
                                 }
                             }
@@ -492,7 +461,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
 
     private void onDiscoverCloseSecureChannel(ConversationContext<OpcuaAPU> context, GetEndpointsResponse message) {
 
-        int transactionId = getTransactionIdentifier(securedConnection.get());
+        int transactionId = getTransactionIdentifier();
 
         ExpandedNodeId expandedNodeId = new ExpandedNodeIdFourByte(false,           //Namespace Uri Specified
             false,            //Server Index Specified
@@ -512,7 +481,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             (byte) 0,
             requestHeader);
 
-        OpcuaCloseRequest closeRequest = new OpcuaCloseRequest(CHUNK,
+        OpcuaCloseRequest closeRequest = new OpcuaCloseRequest(FINAL_CHUNK,
             channelId.get(),
             tokenId.get(),
             transactionId,
@@ -532,7 +501,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
 
     public void onConnectOpenSecureChannel(ConversationContext<OpcuaAPU> context, OpcuaAcknowledgeResponse opcuaAcknowledgeResponse) {
 
-        int transactionId = getTransactionIdentifier(securedConnection.get());
+        int transactionId = getTransactionIdentifier();
 
         ExpandedNodeId expandedNodeId = new ExpandedNodeIdFourByte(false,           //Namespace Uri Specified
                                                                     false,            //Server Index Specified
@@ -573,9 +542,9 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             WriteBuffer buffer = new WriteBuffer(openSecureChannelRequest.getLengthInBytes(), true);
             OpcuaMessageIO.staticSerialize(buffer, openSecureChannelRequest);
 
-            OpcuaOpenRequest openRequest = new OpcuaOpenRequest(CHUNK,
+            OpcuaOpenRequest openRequest = new OpcuaOpenRequest(FINAL_CHUNK,
                 0,
-                new PascalString(this.securityPolicy.length(), this.securityPolicy),
+                new PascalString(this.securityPolicy),
                 this.publicCertificate,
                 this.thumbprint,
                 transactionId,
@@ -585,48 +554,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             OpcuaAPU apu = new OpcuaAPU(openRequest);
 
             if (this.isEncrypted) {
-                LOGGER.info("Encrypting data, Calculating number of blocks");
-                int PREENCRYPTED_BLOCK_LENGTH = 190;
-                int unencryptedLength = apu.getLengthInBytes();
-                LOGGER.info("Unencrypted Length {}", unencryptedLength);
-                int openRequestLength = buffer.getData().length;
-                LOGGER.info("Open Request Length {}", openRequestLength);
-                //The transaction Ids also get encrypted
-                int positionFirstBlock = unencryptedLength - openRequestLength - 8;
-                LOGGER.info("Position of First Block {}", positionFirstBlock);
-                int paddingSize = PREENCRYPTED_BLOCK_LENGTH - ((openRequestLength + 256 + 1 + 8) % PREENCRYPTED_BLOCK_LENGTH);
-                LOGGER.info("Padding Length {}", paddingSize);
-                int preEncryptedLength = openRequestLength + 256 + 1 + 8 + paddingSize;
-                LOGGER.info("Pre-Encrypted Length {}", preEncryptedLength);
-                if (preEncryptedLength % PREENCRYPTED_BLOCK_LENGTH != 0) {
-                    throw new PlcRuntimeException("Pre encrypted block length " + preEncryptedLength + " isn't a multiple of the block size");
-                }
-                int numberOfBlocks = preEncryptedLength / PREENCRYPTED_BLOCK_LENGTH;
-                LOGGER.info("Number of blocks {}", numberOfBlocks);
-                int encryptedLength = numberOfBlocks * 256 + positionFirstBlock;
-                LOGGER.info("Encrypted Length {}", encryptedLength);
-                WriteBuffer buf = new WriteBuffer(encryptedLength, true);
-                OpcuaAPUIO.staticSerialize(buf, apu);
-                byte paddingByte = (byte) paddingSize;
-                LOGGER.info("Writing Padding at position {}", buf.getPos());
-                buf.writeByte(8, paddingByte);
-                for (int i = 0; i < paddingSize; i++) {
-                    buf.writeByte(8, paddingByte);
-                }
-                //Writing Message Length
-                int tempPos = buf.getPos();
-                buf.setPos(4);
-                buf.writeInt(32, encryptedLength);
-                buf.setPos(tempPos);
-                byte[] signature = encryptionHandler.sign(buf.getBytes(0, unencryptedLength + paddingSize + 1));
-                //Write the signature to the end of the buffer
-                //buf.setPos(encryptedLength - 256);
-                for (int i = 0; i < signature.length; i++) {
-                    buf.writeByte(8, signature[i]);
-                }
-                buf.setPos(positionFirstBlock);
-                encryptionHandler.encryptBlock(buf, buf.getBytes(positionFirstBlock, positionFirstBlock + preEncryptedLength));
-                apu = OpcuaAPUIO.staticParse(new ReadBuffer(buf.getData(), true), false);
+                apu = OpcuaAPUIO.staticParse(encryptionHandler.encodeMessage(openRequest, buffer.getData()), false);
             }
 
             context.sendRequest(apu)
@@ -635,43 +563,12 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                 .unwrap(p -> (OpcuaOpenResponse) p.getMessage())
                 .handle(opcuaOpenResponse -> {
                     try {
-                        //Decrypt If needed
-                        ReadBuffer readBuffer = null;
-                        if (isEncrypted) {
-                            int encryptedLength = opcuaOpenResponse.getLengthInBytes();
-                            LOGGER.info("Encrypted Length {}", encryptedLength);
-                            int encryptedMessageLength = opcuaOpenResponse.getMessage().length + 8;
-                            LOGGER.info("Encrypted Message Length {}", encryptedMessageLength);
-                            int headerLength = encryptedLength - encryptedMessageLength;
-                            LOGGER.info("Header Length {}", headerLength);
-                            int numberOfBlocks = encryptedMessageLength / 256;
-                            LOGGER.info("Number of Blocks Length {}", numberOfBlocks);
-                            WriteBuffer buf = new WriteBuffer(headerLength + numberOfBlocks * 256,true);
-                            OpcuaAPUIO.staticSerialize(buf, new OpcuaAPU(opcuaOpenResponse));
-                            byte[] data = buf.getBytes(headerLength, encryptedLength);
-                            buf.setPos(headerLength);
-                            encryptionHandler.decryptBlock(buf, data);
-                            int tempPos = buf.getPos();
-                            LOGGER.info("Position at end of Decryption:- {}", tempPos);
-                            LOGGER.info("Position at end of Decryption within Messgae:- {}", tempPos - headerLength);
-                            buf.setPos(4);
-                            buf.writeInt(32, tempPos - 208 - 1);
-                            //Check Signature, etc..
-                            buf.setPos(0);
-                            byte[] unencrypted = buf.getBytes(0, tempPos - 208);
-                            byte[] signature = buf.getBytes(tempPos - 208, tempPos);
-                            LOGGER.info("Signature Length:- {}" + signature.length);
-                            LOGGER.info("Signature;- {}", signature);
-                            if (!encryptionHandler.checkSignature(unencrypted, signature)) {
-                                LOGGER.info("Signature verification failed: - {}", unencrypted);
-                            }
-                            readBuffer = new ReadBuffer(unencrypted, true);
-                            opcuaOpenResponse = (OpcuaOpenResponse) OpcuaAPUIO.staticParse(readBuffer, true).getMessage();
-                            readBuffer = new ReadBuffer(opcuaOpenResponse.getMessage(), true);
-                        } else {
-                            readBuffer = new ReadBuffer(opcuaOpenResponse.getMessage(), true);
+                        if (this.isEncrypted) {
+                            opcuaOpenResponse = (OpcuaOpenResponse) OpcuaAPUIO.staticParse(encryptionHandler.decodeMessage(opcuaOpenResponse, opcuaOpenResponse.getMessage()), true).getMessage();
                         }
+                        ReadBuffer readBuffer = new ReadBuffer(opcuaOpenResponse.getMessage(), true);
                         OpcuaMessage message = OpcuaMessageIO.staticParse(readBuffer);
+
                         if (message instanceof ServiceFault) {
                             ServiceFault fault = (ServiceFault) message;
                             LOGGER.error("Failed to connect to opc ua server for the following reason:- {}, {}", fault.getResponseHeader().getServiceResult().getStatusCode(), OpcuaStatusCodes.enumForValue(fault.getResponseHeader().getServiceResult().getStatusCode()));
@@ -700,7 +597,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         channelId.set((int) openSecureChannelResponse.getSecurityToken().getChannelId());
 
 
-        int transactionId = getTransactionIdentifier(securedConnection.get());
+        int transactionId = getTransactionIdentifier();
 
         Integer nextSequenceNumber = opcuaOpenResponse.getSequenceNumber() + 1;
         Integer nextRequestId = opcuaOpenResponse.getRequestId() + 1;
@@ -721,16 +618,16 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         LocalizedText applicationName = new LocalizedText((short) 0,
             true,
             true,
-            new PascalString("en".length(), "en"),
-            new PascalString(applicationText.length(), applicationText));
+            new PascalString("en"),
+            APPLICATION_TEXT);
 
         PascalString gatewayServerUri = NULL_STRING;
         PascalString discoveryProfileUri = NULL_STRING;
         int noOfDiscoveryUrls = -1;
         PascalString[] discoveryUrls = new PascalString[0];
 
-        ApplicationDescription clientDescription = new ApplicationDescription(new PascalString(applicationUri.length(), applicationUri),
-            new PascalString(productUri.length(), productUri),
+        ApplicationDescription clientDescription = new ApplicationDescription(APPLICATION_URI,
+            PRODUCT_URI,
             applicationName,
             ApplicationType.applicationTypeClient,
             gatewayServerUri,
@@ -743,9 +640,9 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             requestHeader,
             clientDescription,
             NULL_STRING,
-            new PascalString(endpoint.length(), endpoint),
-            new PascalString(sessionName.length(), sessionName),
-            new PascalByteString(clientNonce.length, clientNonce),
+            this.endpoint,
+            sessionName,
+            new PascalByteString(clientNonce),
             NULL_BYTE_STRING,
             120000L,
             0L);
@@ -754,14 +651,22 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             WriteBuffer buffer = new WriteBuffer(createSessionRequest.getLengthInBytes(), true);
             OpcuaMessageIO.staticSerialize(buffer, createSessionRequest);
 
-            OpcuaMessageRequest messageRequest = new OpcuaMessageRequest(CHUNK,
+            OpcuaMessageRequest messageRequest = new OpcuaMessageRequest(FINAL_CHUNK,
                 channelId.get(),
                 tokenId.get(),
                 nextSequenceNumber,
                 nextRequestId,
                 buffer.getData());
 
-            context.sendRequest(new OpcuaAPU(messageRequest))
+            OpcuaAPU apu = null;
+
+            if (this.isEncrypted) {
+                apu = OpcuaAPUIO.staticParse(encryptionHandler.encodeMessage(messageRequest, buffer.getData()), false);
+            } else {
+                apu = new OpcuaAPU(messageRequest);
+            }
+
+            context.sendRequest(apu)
                 .expectResponse(OpcuaAPU.class, REQUEST_TIMEOUT)
                 .check(p -> p.getMessage() instanceof OpcuaMessageResponse)
                 .unwrap(p -> (OpcuaMessageResponse) p.getMessage())
@@ -796,8 +701,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         senderNonce = createSessionResponse.getServerNonce().getStringValue();
 
         for (EndpointDescription endpointDescription: createSessionResponse.getServerEndpoints()) {
-            LOGGER.info("{} - {}", endpointDescription.getEndpointUrl().getStringValue(), this.endpoint);
-            if (endpointDescription.getEndpointUrl().getStringValue().equals(this.endpoint)) {
+            LOGGER.info("{} - {}", endpointDescription.getEndpointUrl().getStringValue(), this.endpoint.getStringValue());
+            if (endpointDescription.getEndpointUrl().getStringValue().equals(this.endpoint.getStringValue())) {
                 for (UserTokenPolicy identityToken : endpointDescription.getUserIdentityTokens()) {
                     if (identityToken.getTokenType() == UserTokenType.userTokenTypeAnonymous) {
                         if (this.username == null) {
@@ -817,7 +722,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         tokenId.set((int) opcuaMessageResponse.getSecureTokenId());
         channelId.set((int) opcuaMessageResponse.getSecureChannelId());
 
-        int transactionId = getTransactionIdentifier(securedConnection.get());
+        int transactionId = getTransactionIdentifier();
 
         Integer nextSequenceNumber = opcuaMessageResponse.getSequenceNumber() + 1;
         Integer nextRequestId = opcuaMessageResponse.getRequestId() + 1;
@@ -827,7 +732,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             throw new PlcConnectionException("Sequence number isn't as expected, we might have missed a packet. - " +  transactionId + " != " + nextSequenceNumber);
         }
 
-        int requestHandle = getRequestHandle(securedConnection.get());
+        int requestHandle = getRequestHandle();
 
         RequestHeader requestHeader = new RequestHeader(authenticationToken,
             getCurrentDateTime(),
@@ -866,7 +771,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             WriteBuffer buffer = new WriteBuffer(activateSessionRequest.getLengthInBytes(), true);
             OpcuaMessageIO.staticSerialize(buffer, activateSessionRequest);
 
-            OpcuaMessageRequest activateMessageRequest = new OpcuaMessageRequest(CHUNK,
+            OpcuaMessageRequest activateMessageRequest = new OpcuaMessageRequest(FINAL_CHUNK,
                 channelId.get(),
                 tokenId.get(),
                 nextSequenceNumber,
@@ -912,7 +817,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         DefaultPlcReadRequest request = (DefaultPlcReadRequest) readRequest;
 
 
-        int requestHandle = getRequestHandle(securedConnection.get());
+        int requestHandle = getRequestHandle();
 
         RequestHeader requestHeader = new RequestHeader(authenticationToken,
             getCurrentDateTime(),
@@ -936,7 +841,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             } else if (field.getIdentifierType() == OpcuaIdentifierType.GUID_IDENTIFIER) {
                 nodeId = new NodeIdGuid(NodeIdType.nodeIdTypeGuid, new GuidNodeId(field.getNamespace(), toGuidValue(field.getIdentifier())));
             } else if (field.getIdentifierType() == OpcuaIdentifierType.STRING_IDENTIFIER) {
-                nodeId = new NodeIdString(NodeIdType.nodeIdTypeString, new StringNodeId(field.getNamespace(), new PascalString(field.getIdentifier().length(), field.getIdentifier())));
+                nodeId = new NodeIdString(NodeIdType.nodeIdTypeString, new StringNodeId(field.getNamespace(), new PascalString(field.getIdentifier())));
             }
             readValueArray[i] = new ReadValueId(nodeId,
                 0xD,
@@ -952,13 +857,13 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             readValueArray.length,
             readValueArray);
 
-        int transactionId = getTransactionIdentifier(securedConnection.get());
+        int transactionId = getTransactionIdentifier();
 
         try {
             WriteBuffer buffer = new WriteBuffer(opcuaReadRequest.getLengthInBytes(), true);
             OpcuaMessageIO.staticSerialize(buffer, opcuaReadRequest);
 
-            OpcuaMessageRequest readMessageRequest = new OpcuaMessageRequest(CHUNK,
+            OpcuaMessageRequest readMessageRequest = new OpcuaMessageRequest(FINAL_CHUNK,
                 channelId.get(),
                 tokenId.get(),
                 transactionId,
@@ -1387,7 +1292,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                 PascalString[] tmpString = new PascalString[length];
                 for (int i = 0; i < length; i++) {
                     String s = valueObject.getIndex(i).getString();
-                    tmpString[i] = new PascalString(s.length(), s);
+                    tmpString[i] = new PascalString(s);
                 }
                 return new VariantString(length == 1 ? false : true,
                     false,
@@ -1419,7 +1324,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         CompletableFuture<PlcWriteResponse> future = new CompletableFuture<>();
         DefaultPlcWriteRequest request = (DefaultPlcWriteRequest) writeRequest;
 
-        int requestHandle = getRequestHandle(securedConnection.get());
+        int requestHandle = getRequestHandle();
 
         RequestHeader requestHeader = new RequestHeader(authenticationToken,
             getCurrentDateTime(),
@@ -1443,7 +1348,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             } else if (field.getIdentifierType() == OpcuaIdentifierType.GUID_IDENTIFIER) {
                 nodeId = new NodeIdGuid(NodeIdType.nodeIdTypeGuid, new GuidNodeId(field.getNamespace(), toGuidValue(field.getIdentifier())));
             } else if (field.getIdentifierType() == OpcuaIdentifierType.STRING_IDENTIFIER) {
-                nodeId = new NodeIdString(NodeIdType.nodeIdTypeString, new StringNodeId(field.getNamespace(), new PascalString(field.getIdentifier().length(), field.getIdentifier())));
+                nodeId = new NodeIdString(NodeIdType.nodeIdTypeString, new StringNodeId(field.getNamespace(), new PascalString(field.getIdentifier())));
             }
             writeValueArray[i] = new WriteValue(nodeId,
                 0xD,
@@ -1470,13 +1375,13 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             writeValueArray.length,
             writeValueArray);
 
-        int transactionId = getTransactionIdentifier(securedConnection.get());
+        int transactionId = getTransactionIdentifier();
 
         try {
             WriteBuffer buffer = new WriteBuffer(opcuaWriteRequest.getLengthInBytes(), true);
             OpcuaMessageIO.staticSerialize(buffer, opcuaWriteRequest);
 
-            OpcuaMessageRequest writeMessageRequest = new OpcuaMessageRequest(CHUNK,
+            OpcuaMessageRequest writeMessageRequest = new OpcuaMessageRequest(FINAL_CHUNK,
                 channelId.get(),
                 tokenId.get(),
                 transactionId,
@@ -1536,45 +1441,37 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     }
 
     /**
-     * Returns the next transaction identifier, for a secured connection we should return a random number.
-     * @param random - Select if we return a random number or the next value
-     * @return
+     * Returns the next transaction identifier.
+     *
+     * @return the next sequential transaction identifier
      */
-    private int getTransactionIdentifier(boolean random) {
-        if (random) {
-            return ThreadLocalRandom.current().nextInt();
-        } else {
-            int transactionId = transactionIdentifierGenerator.getAndIncrement();
-            if(transactionIdentifierGenerator.get() == DEFAULT_MAX_REQUEST_ID) {
-                transactionIdentifierGenerator.set(1);
-            }
-            return transactionId;
+    private int getTransactionIdentifier() {
+        int transactionId = transactionIdentifierGenerator.getAndIncrement();
+        if(transactionIdentifierGenerator.get() == DEFAULT_MAX_REQUEST_ID) {
+            transactionIdentifierGenerator.set(1);
         }
+        return transactionId;
     }
 
     /**
-     * Returns the next request handle, for a secured connection we should return a random number.
-     * @param random - Select if we return a random number or the next value
-     * @return
+     * Returns the next request handle
+     *
+     * @return the next sequential request handle
      */
-    private int getRequestHandle(boolean random) {
-        if (random) {
-            return ThreadLocalRandom.current().nextInt();
-        } else {
-            int transactionId = requestHandleGenerator.getAndIncrement();
-            if(requestHandleGenerator.get() == DEFAULT_MAX_REQUEST_ID) {
-                requestHandleGenerator.set(1);
-            }
-            return transactionId;
+    private int getRequestHandle() {
+        int transactionId = requestHandleGenerator.getAndIncrement();
+        if(requestHandleGenerator.get() == DEFAULT_MAX_REQUEST_ID) {
+            requestHandleGenerator.set(1);
         }
+        return transactionId;
     }
 
     private long getCurrentDateTime() {
-        return (System.currentTimeMillis() * 10000) + epochOffset;
+        return (System.currentTimeMillis() * 10000) + EPOCH_OFFSET;
     }
 
     private long getDateTime(long dateTime) {
-        return (dateTime - epochOffset) / 10000;
+        return (dateTime - EPOCH_OFFSET) / 10000;
     }
 
     /**
@@ -1616,10 +1513,10 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
 
                 byte[] encryptedPassword = encryptionHandler.encryptPassword(encodeablePassword);
                 UserNameIdentityToken userNameIdentityToken =  new UserNameIdentityToken(
-                    new PascalString("username".length(), "username"),
-                    new PascalString(this.username.length(), this.username),
+                    new PascalString("username"),
+                    new PascalString(this.username),
                     new PascalByteString(encryptedPassword.length, encryptedPassword),
-                    new PascalString(PASSWORD_ENCRYPTION_ALGORITHM.length(), PASSWORD_ENCRYPTION_ALGORITHM)
+                    new PascalString(PASSWORD_ENCRYPTION_ALGORITHM)
                 );
                 WriteBuffer bufferUserName = new WriteBuffer(userNameIdentityToken.getLengthInBytes(), true);
                 try{
diff --git a/protocols/opcua/src/main/resources/protocols/opcua/opcua.mspec b/protocols/opcua/src/main/resources/protocols/opcua/opcua.mspec
index d3ace36..4a9442e 100644
--- a/protocols/opcua/src/main/resources/protocols/opcua/opcua.mspec
+++ b/protocols/opcua/src/main/resources/protocols/opcua/opcua.mspec
@@ -27,23 +27,22 @@
     [discriminator string '24'          'messageType']
     [typeSwitch 'messageType','response'
         ['HEL','false'     OpcuaHelloRequest
-            [simple          string '1 * 8'            'chunk']
-            [implicit          int 32             'messageSize' 'lengthInBytes']
+            [simple          string '8'         'chunk']
+            [implicit        int 32             'messageSize' 'lengthInBytes']
             [simple          int 32             'version']
             [simple          int 32             'receiveBufferSize']
             [simple          int 32             'sendBufferSize']
             [simple          int 32             'maxMessageSize']
             [simple          int 32             'maxChunkCount']
-            [simple          int 32             'stringLength']
-            [simple          string 'stringLength * 8' 'endpoint']
+            [simple          PascalString       'endpoint']
         ]
         ['HEL','true'     OpcuaHelloResponse
         ]
         ['ACK','false'     OpcuaAcknowledgeRequest
         ]
         ['ACK','true'     OpcuaAcknowledgeResponse
-            [simple          string '8'            'chunk']
-            [implicit          int 32             'messageSize' 'lengthInBytes']
+            [simple          string '8'         'chunk']
+            [implicit        int 32             'messageSize' 'lengthInBytes']
             [simple          int 32             'version']
             [simple          int 32             'receiveBufferSize']
             [simple          int 32             'sendBufferSize']
@@ -63,21 +62,18 @@
             [simple          PascalByteString   'receiverCertificateThumbprint']
             [simple          int 32             'sequenceNumber']
             [simple          int 32             'requestId']
-            [array           int 8              'message' count 'messageSize - endpoint.stringLength - senderCertificate.stringLength - receiverCertificateThumbprint.stringLength - 32']
+            [array           int 8              'message' count 'messageSize - (endpoint.stringLength == -1 ? 0 : endpoint.stringLength ) - (senderCertificate.stringLength == -1 ? 0 : senderCertificate.stringLength) - (receiverCertificateThumbprint.stringLength == -1 ? 0 : receiverCertificateThumbprint.stringLength) - 32']
        ]
        ['OPN','true'     OpcuaOpenResponse
            [simple          string '8'         'chunk']
            [implicit        int 32             'messageSize' 'lengthInBytes']
            [simple          int 32             'secureChannelId']
-           [simple          int 32             'securityPolicyUriSize']
-           [simple          string 'securityPolicyUriSize == -1 ? 0 : securityPolicyUriSize * 8'          'endpoint']
-           [simple          int 32             'senderCertificateSize']
-           [simple          string 'senderCertificateSize == -1 ? 0 : senderCertificateSize * 8'             'senderCertificate']
-           [simple          int 32             'receiverCertificateThumbprintSize']
-           [simple          string 'receiverCertificateThumbprintSize == -1 ? 0 : receiverCertificateThumbprintSize * 8'             'receiverCertificateThumbprint']
+           [simple          PascalString       'securityPolicyUri']
+           [simple          PascalByteString   'senderCertificate']
+           [simple          PascalByteString   'receiverCertificateThumbprint']
            [simple          int 32             'sequenceNumber']
            [simple          int 32             'requestId']
-           [array           int 8              'message' count 'messageSize - (receiverCertificateThumbprintSize == -1 ? 0 : receiverCertificateThumbprintSize) - (senderCertificateSize == -1 ? 0 : senderCertificateSize) - (securityPolicyUriSize == -1 ? 0 : securityPolicyUriSize) - 32)']
+           [array           int 8              'message' count 'messageSize - (securityPolicyUri.stringLength == -1 ? 0 : securityPolicyUri.stringLength) - (senderCertificate.stringLength == -1 ? 0 : senderCertificate.stringLength) - (receiverCertificateThumbprint.stringLength == -1 ? 0 : receiverCertificateThumbprint.stringLength) - 32)']
        ]
        ['CLO','false'     OpcuaCloseRequest
            [simple          string '8'         'chunk']
diff --git a/protocols/opcua/src/main/xslt/opc-types.xsl b/protocols/opcua/src/main/xslt/opc-types.xsl
index 2dafce0..f0e998c 100644
--- a/protocols/opcua/src/main/xslt/opc-types.xsl
+++ b/protocols/opcua/src/main/xslt/opc-types.xsl
@@ -407,8 +407,9 @@
 ]
 
 [type 'PascalString'
-    [simple int 32 'stringLength']
-    [optional string 'stringLength == -1 ? 0 : stringLength * 8' 'UTF-8' 'stringValue' 'stringLength >= 0']
+    [implicit int 32 'stringLength' 'stringValue.lengthInBytes == 0 ? -1 : stringValue.lengthInBytes']
+    [virtual int 32 'virtualStringLength' 'stringLength']
+    [optional string 'lengthInBits == -1 ? 0 : lengthInBits' 'UTF-8' 'stringValue' 'lengthInBits >= 0']
 ]
 
 [type 'PascalByteString'