2

I'm writing an OPC UA Client in C# using OPC Foundation NuGet package.

I can succesfully connect to server, read and write variables and create subscriptions and monitored items.

Since I'd like to use this code in production environments, I need to use secure connection (username / password policy instead of anonymous connection).

This is how I'm doing it:

    bool security = true; // Set here security enabled
    Session session; // OPC UA Session

    // 1. Generate client application
    ApplicationInstance application = new() { ApplicationType = ApplicationType.Client };

    // 2. Load application configuration
    ClientConfiguration clientConfiguration = new() { DefaultSessionTimeout = _timeout };
    ApplicationConfiguration configuration = new() { ApplicationName = "OPC Foundation SDK Personal Implementation", ClientConfiguration = clientConfiguration, ApplicationUri = "urn:OPC Foundation SDK Personal Implementation" };
    application.ApplicationConfiguration = configuration;

    // 3. Get the endpoint by connecting to server's discovery endpoint
    EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint($"opc.tcp://{Target}", security);

    // 4. Get server certificate
    var rawCertificate = endpointDescription.ServerCertificate;
    CertificateIdentifier serverCertificate = new(rawCertificate);

    // 5. Add server certificate to trusted peers and trusted issuers
    configuration.SecurityConfiguration.TrustedPeerCertificates.TrustedCertificates.Add(serverCertificate);
    configuration.SecurityConfiguration.TrustedIssuerCertificates.TrustedCertificates.Add(serverCertificate);

    // 6. Create application instance certificate
    var appCertificate = new X509Certificate2("TestCertificate.der", "");
    configuration.SecurityConfiguration.AddAppCertToTrustedStore = true;
    configuration.SecurityConfiguration.ApplicationCertificate = new(appCertificate);
    // Validate the configuration
    configuration.Validate(ApplicationType.Client);
    var result = application.CheckApplicationInstanceCertificate(true, CertificateFactory.DefaultKeySize);

    // 7. Setup endpoint
    EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(configuration);
    ConfiguredEndpoint endpoint = new(null, endpointDescription, endpointConfiguration);

    // 8. Create session
    if (security)
        session = Session.Create(configuration, endpoint, false, false, configuration.ApplicationName, (uint)configuration.ClientConfiguration.DefaultSessionTimeout, new UserIdentity("****", "****"), null).Result; // Username and password
    else
        session = Session.Create(configuration, endpoint, false, false, configuration.ApplicationName, (uint)configuration.ClientConfiguration.DefaultSessionTimeout, new UserIdentity(), null).Result; // Anonymous login to OPC UA server

If security is set to true, I get error "certificate chain validation incomplete". This is referred at my application instance certificate because without security the session is created successfully (so I'm sure the server certificate is being accepted).

The certificate I'm using is self-signed and it is generated with the following code:

public static void CreateApplicationCertificate(string certFilename, string keyFilename)
{
    const string CRT_HEADER = "-----BEGIN CERTIFICATE-----\n";
    const string CRT_FOOTER = "\n-----END CERTIFICATE-----";

    const string KEY_HEADER = "-----BEGIN RSA PRIVATE KEY-----\n";
    const string KEY_FOOTER = "\n-----END RSA PRIVATE KEY-----";

    using var rsa = RSA.Create();
    var certRequest = new CertificateRequest("cn=TestCertificate", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

    // This is a test and the certificate is being regenerated every time I run the code, so one day is enough.
    var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(1));

    // Export the private key
    var privateKey = Convert.ToBase64String(rsa.ExportRSAPrivateKey(), Base64FormattingOptions.InsertLineBreaks);
    File.WriteAllText(keyFilename + ".pem", KEY_HEADER + privateKey + KEY_FOOTER);

    // Export the certificate
    var exportData = certificate.Export(X509ContentType.Cert);
    var crt = Convert.ToBase64String(exportData, Base64FormattingOptions.InsertLineBreaks);
    File.WriteAllText(certFilename + ".der", CRT_HEADER + crt + CRT_FOOTER);
}

I added this line of code

configuration.SecurityConfiguration.AddAppCertToTrustedStore = true;

but nothing changed.

Any help is really appreciated.

IFrank
  • 419
  • 1
  • 5
  • 12
  • I think it would help if you can find you if the problem is with the server or client certificate chain. Can you connect with an other client e.g. UaExpert with security on? – SFriedl Feb 20 '23 at 07:25

1 Answers1

1

After hours of struggling with the same problem the solution turned out to be very simple.

For example the Ip I am connecting to: opc.tcp://192.168.54.200:4840/

I am connecting to port 4840, after you have the port you need to go to your firewall advanced settings:

firewall

After this add one rule for inbound and one rule for outbound connections:

new rule

Click on Port and click on next. After this select TCP and the port you have. Mine was 4840 and add it. Do this for incoming and outgoing (or inbound and outbound) connections!!!

This should fix the error.

Victor Pieper
  • 540
  • 2
  • 17