Skip to content

[Feature Request]: Basic256Sha256 policy not implemented? #1844

Closed
@sciortid

Description

@sciortid

What would you like to happen?

Hello! First of all I'm pretty sure I'm missing something but i'll try.

I created an OPCUA Python server with username or certificates authentication policies. It has been tested with OPCUA Expert and i can access it with both policies methods.

I (and ChatGPT) created a simple standalone example to connect to the server:

package opcua;
import org.apache.plc4x.java.api.PlcConnection;
import org.apache.plc4x.java.api.messages.PlcReadRequest;
import org.apache.plc4x.java.api.messages.PlcReadResponse;
import org.apache.plc4x.java.DefaultPlcDriverManager;

import java.util.concurrent.CompletableFuture;

public class OpcUaClientWithCert {

    public static void main(String[] args) {
        // URL del server OPC UA, con certificato client e chiave privata inclusi
        String serverUrl = "opcua:tcp://127.0.0.1:4840";
        String discovery = "true";
        String securityPolicy = "Basic256Sha256";  // Percorso del certificato del client
        String clientKeyStore = ".../client_keystore.p12";
        String clientKeySotrePass = "clientKeystorePassword";         // Password della chiave

        // Modificare la stringa di connessione per includere certificato e chiave privata
        String connectionString = String.format(
                "%s?discovery=%s&security-policy=%s&key-store-file=%s&key-store-password=%s",
                serverUrl, discovery, securityPolicy, clientKeyStore, clientKeySotrePass
        );

        String nodeId = "ns=2;i=2";  // Sostituire con il nodo che si desidera leggere

        // Connettersi al server OPC UA con certificati
        DefaultPlcDriverManager driverManager = new DefaultPlcDriverManager(); // Crea un'istanza di DefaultPlcDriverManager
		try (PlcConnection connection = driverManager.getConnection(connectionString)) {
            if (connection != null && connection.isConnected()) {
                System.out.println("Connessione stabilita con il server OPC UA usando certificati.");

                // Creare una richiesta di lettura
                PlcReadRequest.Builder builder = connection.readRequestBuilder();
                builder.addTagAddress("myNode", nodeId);  // Aggiungere il nodo da leggere

                PlcReadRequest readRequest = builder.build();

                // Eseguire la lettura in modo asincrono
                CompletableFuture<? extends PlcReadResponse> future = readRequest.execute();
                PlcReadResponse response = future.get();

                // Stampare il risultato
                System.out.println("Valore letto dal nodo: " + response.getObject("myNode"));
            } else {
                System.out.println("Connessione fallita.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And this is the pom.xml file:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  
  <modelVersion>4.0.0</modelVersion>
  <groupId>test</groupId>
  <artifactId>opcua</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>test</name>
  <description>test</description>
  
	<dependencies>
	
		<dependency>
				<groupId>org.apache.plc4x</groupId>
				<artifactId>plc4j-api</artifactId>
				<version>0.12.0</version>
		</dependency>
		
		<dependency>
			  <groupId>org.apache.plc4x</groupId>
			  <artifactId>plc4j-driver-opcua</artifactId>
			  <version>0.12.0</version>
		</dependency>
		
		<dependency>
			    <groupId>org.apache.plc4x</groupId>
			    <artifactId>plc4j-transport-tcp</artifactId>
			    <version>0.12.0</version>
		</dependency>
		
	    <dependency>
	        <groupId>org.slf4j</groupId>
	        <artifactId>slf4j-api</artifactId>
	        <version>1.7.36</version> 
	    </dependency>
	    
	    <dependency>
	        <groupId>org.slf4j</groupId>
	        <artifactId>slf4j-simple</artifactId>
	        <version>1.7.36</version>
	    </dependency>

	</dependencies>
</project>

When it runs, I get the "Unable to find endpoint.." exception in this part of SecureChannel.java

    private void selectEndpoint(CreateSessionResponse sessionResponse) throws PlcRuntimeException {
        // Get a list of the endpoints which match ours.
        Stream<EndpointDescription> filteredEndpoints = sessionResponse.getServerEndpoints().stream()
            .map(e -> (EndpointDescription) e)
            .filter(this::isEndpoint);

        //Determine if the requested security policy is included in the endpoint
        filteredEndpoints.forEach(endpoint -> hasIdentity(
            endpoint.getUserIdentityTokens().stream()
                .map(p -> (UserTokenPolicy) p)
                .toArray(UserTokenPolicy[]::new)
        ));

        if (this.policyId == null) {
            throw new PlcRuntimeException("Unable to find endpoint - " + this.endpoints.get(0));
        }
        if (this.tokenType == null) {
            throw new PlcRuntimeException("Unable to find Security Policy for endpoint - " + this.endpoints.get(0));
        }
    }

And noticed this is because the policyId is set to null but because apparently the hasIdentity function has no option for policy other than anonymous and username:

    private void hasIdentity(UserTokenPolicy[] policies) {
        for (UserTokenPolicy identityToken : policies) {
            if ((identityToken.getTokenType() == UserTokenType.userTokenTypeAnonymous) && (this.username == null)) {
                policyId = identityToken.getPolicyId();
                tokenType = identityToken.getTokenType();
            } else if ((identityToken.getTokenType() == UserTokenType.userTokenTypeUserName) && (this.username != null)) {
                policyId = identityToken.getPolicyId();
                tokenType = identityToken.getTokenType();
            }
        }
    }

The policies that my server returns correspond to the ones i set with the python script:

image
image

What am I missing? Shouldn't the library allow this kind of access with just the cetificate? I expected other "if" for certificate policies. Did i end up in other functions that should not be used for this policies? In this case, is there something wrong with the main function?

Please note that the example works if i modify the connection string by just adding the username & password, correctly reading the node value:

      String serverUrl = "opcua:tcp://127.0.0.1:4840";
       String discovery = "true";
       String securityPolicy = "Basic256Sha256";  // Percorso del certificato del client
       String clientKeyStore = "..../client_keystore.p12";
       String clientKeySotrePass = "clientKeystorePassword";         // Password della chiave
       String userName = "user1";
       String password = "password1";
       // Modificare la stringa di connessione per includere certificato e chiave privata
       String connectionString = String.format(
               "%s?discovery=%s&security-policy=%s&key-store-file=%s&key-store-password=%s&username=%s&password=%s",
               serverUrl, discovery, securityPolicy, clientKeyStore, clientKeySotrePass, userName, password
       );

But it does not surprise me because it is using the username policy in this case (even if Basic256Sha256 is still specified)

Programming Languages

  • plc4j
  • plc4go
  • plc4c
  • plc4net

Protocols

  • AB-Ethernet
  • ADS /AMS
  • BACnet/IP
  • CANopen
  • DeltaV
  • DF1
  • EtherNet/IP
  • Firmata
  • KNXnet/IP
  • Modbus
  • OPC-UA
  • S7

Metadata

Metadata

Assignees

Labels

OPC-UAhttps://plc4x.apache.org/users/protocols/opcua.htmljavaPull requests that update Java codepythonPull requests that update Python code

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions