OPC UA标签收集(OPCFoundation NETStandard库,代码修正)

问题描述 投票:0回答:1

任务

最终的项目是工作应用程序(运行时:net6.0,目标操作系统:Windows Server 2012R2),旨在用作 Windows 服务,使用 OPC UA 协议从 KEPServer(内部系统的代理)收集数据。

OPC UA 服务器设置

支持的安全策略:

  1. Basic256Sha256:签名;签名并加密。
  2. Basic256:签名;签名并加密。
  3. Basic128Rsa15:签名;签名并加密。

服务器管理员向我保证不会使用带有私钥的证书。使用的扩展是

.der
类型。

我的配置

出于安全考虑,敏感数据已被“端口”、“IP”等虚拟存根替代。

<?xml version="1.0" encoding="utf-8"?>
<ApplicationConfiguration
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd"
  xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd"
>
  <ApplicationName>OPCFoundationConsoleClient</ApplicationName>
  <ApplicationUri>urn:localhost:OPCFoundationConsoleClient</ApplicationUri>
  <ApplicationType>Client_1</ApplicationType>
  
  <SecurityConfiguration>

    <ApplicationCertificate>
      <StoreType>Directory</StoreType>
      <StorePath>%LocalFolder%/CertificateStores/pki/own</StorePath>
      <SubjectName>CN=MY_CLIENT, C=Country, S=City, O=organization, DC=opc.tcp://ip:port</SubjectName>
      <!--<Thumbprint>Some hash</Thumbprint>-->
    </ApplicationCertificate>

    <TrustedIssuerCertificates>
      <StoreType>Directory</StoreType>
      <StorePath>%LocalFolder%/CertificateStores/pki/issuer</StorePath>
    </TrustedIssuerCertificates>

    <TrustedPeerCertificates>
      <StoreType>Directory</StoreType>
      <StorePath>%LocalFolder%/CertificateStores/pki/trusted</StorePath>
    </TrustedPeerCertificates>

    <RejectedCertificateStore>
      <StoreType>Directory</StoreType>
      <StorePath>%LocalFolder%/CertificateStores/pki/rejected</StorePath>
    </RejectedCertificateStore>

    <AutoAcceptUntrustedCertificates>false</AutoAcceptUntrustedCertificates>

  </SecurityConfiguration>
  
  <TransportConfigurations></TransportConfigurations>
  
  <TransportQuotas>
    <OperationTimeout>120000</OperationTimeout>
    <MaxStringLength>4194304</MaxStringLength>
    <MaxByteStringLength>4194304</MaxByteStringLength>
    <MaxArrayLength>65535</MaxArrayLength>
    <MaxMessageSize>4194304</MaxMessageSize>
    <MaxBufferSize>65535</MaxBufferSize>
    <ChannelLifetime>300000</ChannelLifetime>
    <SecurityTokenLifetime>3600000</SecurityTokenLifetime>
  </TransportQuotas>

  <ClientConfiguration>
    <DefaultSessionTimeout>60000</DefaultSessionTimeout>
    
    <WellKnownDiscoveryUrls>
      <ua:String>opc.tcp://{0}:port</ua:String>
    </WellKnownDiscoveryUrls>
   
    <DiscoveryServers></DiscoveryServers>
    <MinSubscriptionLifetime>10000</MinSubscriptionLifetime>

    <OperationLimits>
      <MaxNodesPerRead>2500</MaxNodesPerRead>
      <MaxNodesPerHistoryReadData>1000</MaxNodesPerHistoryReadData>
      <MaxNodesPerHistoryReadEvents>1000</MaxNodesPerHistoryReadEvents>
      <MaxNodesPerWrite>2500</MaxNodesPerWrite>
      <MaxNodesPerHistoryUpdateData>1000</MaxNodesPerHistoryUpdateData>
      <MaxNodesPerHistoryUpdateEvents>1000</MaxNodesPerHistoryUpdateEvents>
      <MaxNodesPerMethodCall>2500</MaxNodesPerMethodCall>
      <MaxNodesPerBrowse>2500</MaxNodesPerBrowse>
      <MaxNodesPerRegisterNodes>2500</MaxNodesPerRegisterNodes>
      <MaxNodesPerTranslateBrowsePathsToNodeIds>2500</MaxNodesPerTranslateBrowsePathsToNodeIds>
      <MaxNodesPerNodeManagement>2500</MaxNodesPerNodeManagement>
      <MaxMonitoredItemsPerCall>2500</MaxMonitoredItemsPerCall>
    </OperationLimits>

  </ClientConfiguration>
  
</ApplicationConfiguration>

到目前为止一切顺利。主题已定。设置存储文件夹的路径。我还将 KEPServer 的证书添加到 .CertificatesStores/pki/issuer/certs。应用程序证书已传递给服务器管理员以添加到受信任列表。他还表示 PHD 重新初始化期间没有出现错误。

我的代码示例

在查看了 UA-.NETStandard-Samples 中的“ConsoleReferenceClient”(因为它对我来说最合适)存储库后,我删除了以下代码行:

using System.Text.Json;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using Microsoft.Extensions.Configuration;
using Models;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;

const string tagsDataSettingsName = "tagsDataSettings.json"; // tags' ID list and other relevant settings

try {
 /* configuration handling logic */
  var configuration = await application.LoadApplicationConfiguration(silent: false); 
  var haveAppCertificate = await application.CheckApplicationInstanceCertificate(true, 2048);
  if (!haveAppCertificate) {
    throw new Exception("Application certificate isn't found."); 
  } 
  var endpointDescription = CoreClientUtils.SelectEndpoint(configuration, tds.Host.Address, true); // tds is a custom settings variable
  endpointDescription.SecurityMode = (MessageSecurityMode)tds.Host.SecurityMode;
  endpointDescription.SecurityPolicyUri = tds.Host.SecurityPolicyUri;  
  var endpointConfiguration = EndpointConfiguration.Create(configuration);  
  ConfiguredEndpoint endpoint = new(null, endpointDescription, endpointConfiguration); 
  var certificate = configuration.SecurityConfiguration.ApplicationCertificate.Certificate;
  using (var session = await TraceableSessionFactory.Instance.CreateAsync(configuration: configuration, endpoint: endpoint, updateBeforeConnect: true, checkDomain: false, sessionName: "", sessionTimeout: 60000, identity: new UserIdentity(certificate), preferredLocales: null)) {
    if (session!.Connected) {
      var readValues = tds.TagGroups?.Aggregate(new ReadValueIdCollection(), 
        (final, current) => {
          Array.ForEach(current.Tags!, 
            id => {
              var tag = new ReadValueId() 
              { 
                NodeId = new NodeId(id), 
                AttributeId = Attributes.Value 
              };
              final.Add(tag);
             });
          return final;
        });
      var response = await session.ReadAsync(null, 0, TimestampsToReturn.Both, readValues, CancellationToken.None);
      var formattedData = response.Results.Aggregate("", 
        (final, current) => { 
          var tagData = $"[{current.SourceTimestamp}]: status code: {current.StatusCode}; value: {current.Value}";
          final = (final == "") ? $"{tagData}" : $"{final}\n{tagData}"; 
          return final;
        });
      Console.WriteLine(formattedData);
      await session!.CloseAsync();
    }
  }
  Console.ReadKey();
} catch (AggregateException agrErr) {
  foreach (var ie in agrErr.InnerExceptions) {
    Console.WriteLine($"AggregatedException.InnerExceptions error: {ie.Message}"); 
  }
  Console.ReadKey();
} catch (Exception err) {
  Console.WriteLine($"Error: {err.Message}"); 
  Console.ReadKey();
}
var i = 2;
while (i > 0) {
  Console.WriteLine($"The application shuts down in {i} s");
  await Task.Delay(1000);
  i--;
} 

tds
对象是记录类型,用于标签ID(ns=2;s=Namespace.Device.TagName)存储以及服务器地址。它还包含安全策略 URL 和安全模式类型。这是我的
.json
格式的自定义配置。

问题描述

当我在生产服务器上部署并启动应用程序时,出现错误:

Endpoint doesn't support the user identity type provided
。创建应用程序证书:
.der
own 子文件夹和
.pfx
- private。据我了解,该消息很简单:服务器不想允许连接。

我尝试测试与 UaExpert 程序的连接,但是当我设置 身份验证设置 => 证书时,出现错误,提示未找到 X509 证书私钥。该程序也不允许使用生成的

.pfx
证书。文件浏览器中仅允许使用
.pem
。所以它没有给我带来任何显着的结果。选择策略时,我只能在服务器设置块中使用上述内容。

certificate.PrivateKey
返回
true
,但根据管理员的要求我不允许使用它。

certificate.GetHashString()
返回正确的哈希值。我想应用程序尝试使用
.pfx
证书,但我如何使用常规
.der
证书?

我该怎么办?将

.der
转换为
.pem
.pfx
是密钥存储,所以我可以以某种方式从中提取密钥?我希望有更详细的规格。

所以,我可以总结以下几点:

  1. 如何使用此类输入参数获取打开的连接?
  2. 如果可能的话,如何设置证书到期日期?一些主题属性(我还没有找到相关提示)?我是否会被迫每年将新证书传递给管理员(默认情况下,自创建日期起 1 年)?
  3. 当我尝试格式化服务器响应的输出时,我没有看到任何 ID 属性。如何将标签 ID 数据添加到响应字符串?如果要写入DB,如何区分标签值?

附注我在这个网站上遇到过一些示例,但它们不使用证书(匿名身份),而是使用项目监控,而我需要按时间间隔(计时器)进行会话。

c# .net-6.0 client-certificates opc-ua
1个回答
0
投票

您似乎混淆了 OPC UA application 身份验证(始终使用证书完成)与 user 身份验证,这是一个不同的概念。研究 OPC UA 规范以了解这些规范。 用户身份验证可以是匿名、用户名/密码,也可以是(不太常见)证书。

您未正确地将 UserIdentity 中的 OPC UA application 证书传递到会话。这不仅是错误的证书,而且据我所知,KepServer 根本不支持用于 user 身份验证的 X509 证书。您需要使用匿名或用户名/密码。和, AFAIK,KepServer 默认情况下不允许匿名 user 连接。

因此,为了解决您的问题,您需要

  1. 使用正确的用户名和密码创建一个 UserIdentity,而不是证书,并将其传递给会话创建调用,或者

  2. 在 KepServer 中允许匿名用户连接(编辑 -> 属性;OPC UA -> 允许匿名登录),然后创建一个空(匿名)UserIdentity 并将其传递给会话创建调用。

© www.soinside.com 2019 - 2024. All rights reserved.