OPC UA客户端:客户端证书 “证书链验证不完整”(使用OPC Foundation的SDK)

huangapple go评论76阅读模式
英文:

OPC UA Client: client certificate "certificate chain validation incomplete" (using SDK from OPC Foundation)

问题

我正在使用OPC Foundation NuGet包在C#中编写OPC UA客户端。

我可以成功连接到服务器,读取和写入变量,并创建订阅和监视项目。

由于我希望在生产环境中使用此代码,我需要使用安全连接(用户名/密码策略而不是匿名连接)。

这是我如何做的:

bool security = true; // 在此处设置安全连接
Session session; // OPC UA会话

// 1. 生成客户端应用程序
ApplicationInstance application = new() { ApplicationType = ApplicationType.Client };

// 2. 加载应用程序配置
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. 通过连接到服务器的发现端点获取终端描述
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint($"opc.tcp://{Target}", security);

// 4. 获取服务器证书
var rawCertificate = endpointDescription.ServerCertificate;
CertificateIdentifier serverCertificate = new(rawCertificate);

// 5. 将服务器证书添加到受信任的对等体和受信任的发行者
configuration.SecurityConfiguration.TrustedPeerCertificates.TrustedCertificates.Add(serverCertificate);
configuration.SecurityConfiguration.TrustedIssuerCertificates.TrustedCertificates.Add(serverCertificate);

// 6. 创建应用程序实例证书
var appCertificate = new X509Certificate2("TestCertificate.der", "");
configuration.SecurityConfiguration.AddAppCertToTrustedStore = true;
configuration.SecurityConfiguration.ApplicationCertificate = new(appCertificate);
// 验证配置
configuration.Validate(ApplicationType.Client);
var result = application.CheckApplicationInstanceCertificate(true, CertificateFactory.DefaultKeySize);

// 7. 设置终端
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(configuration);
ConfiguredEndpoint endpoint = new(null, endpointDescription, endpointConfiguration);

// 8. 创建会话
if (security)
    session = Session.Create(configuration, endpoint, false, false, configuration.ApplicationName, (uint)configuration.ClientConfiguration.DefaultSessionTimeout, new UserIdentity("****", "****"), null).Result; // 用户名和密码
else
    session = Session.Create(configuration, endpoint, false, false, configuration.ApplicationName, (uint)configuration.ClientConfiguration.DefaultSessionTimeout, new UserIdentity(), null).Result; // 匿名登录到OPC UA服务器

如果security设置为true,我会收到“证书链验证不完整”的错误。这是指我的应用程序实例证书,因为没有安全性,会话成功创建(因此我确信服务器证书被接受)。

我使用的证书是自签名的,是使用以下代码生成的:

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);

    // 这是一个测试,证书在每次运行代码时都会重新生成,所以一天足够了。
    var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(1));

    // 导出私钥
    var privateKey = Convert.ToBase64String(rsa.ExportRSAPrivateKey(), Base64FormattingOptions.InsertLineBreaks);
    File.WriteAllText(keyFilename + ".pem", KEY_HEADER + privateKey + KEY_FOOTER);

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

我添加了以下代码行:

configuration.SecurityConfiguration.AddAppCertToTrustedStore = true;

但没有任何变化。

非常感谢任何帮助。

英文:

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.

答案1

得分: 1

经过数小时的努力解决了同样的问题,解决方案竟然非常简单。

例如,我连接到的IP地址是:opc.tcp://192.168.54.200:4840/

我连接到了端口4840,在获得端口后,您需要进入防火墙的高级设置:

OPC UA客户端:客户端证书 “证书链验证不完整”(使用OPC Foundation的SDK)

然后添加一个入站规则和一个出站规则:

OPC UA客户端:客户端证书 “证书链验证不完整”(使用OPC Foundation的SDK)

点击“端口”,然后点击“下一步”。然后选择TCP和您的端口。我的是4840,添加它。请对入站和出站连接都执行此操作!

这应该解决了错误。

英文:

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:

OPC UA客户端:客户端证书 “证书链验证不完整”(使用OPC Foundation的SDK)

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

OPC UA客户端:客户端证书 “证书链验证不完整”(使用OPC Foundation的SDK)

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.

huangapple
  • 本文由 发表于 2023年1月9日 17:43:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75055448.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定