【问题标题】:Golang Opc UA Client Implementation with Certificate带有证书的 Golang Opc UA 客户端实现
【发布时间】:2022-01-04 14:26:19
【问题描述】:

我正在尝试使用证书连接我的本地 Kepware OPC UA 服务器。 OPC UA 服务器安全信息:

在 golang app 中,我使用“github.com/gopcua/opcua/ua” opc 客户端包。我创建密钥对并为它们提供变量路径并在配置中使用它们:

    opts := []opcua.Option{
            opcua.SecurityPolicy(relatedConnection.SecurityPolicy),
            opcua.SecurityModeString(relatedConnection.SecurityMode),
            opcua.CertificateFile(certFile),
            opcua.PrivateKeyFile(keyFile),
            opcua.AutoReconnect(true),
            opcua.ReconnectInterval(time.Second * 5),
            opcua.RequestTimeout(time.Second * 3),
        }

当安全策略和模式都没有时,我可以毫无问题地连接到服务器。当我选择这些安全策略时,我不知道如何实现代码或 ssl 密钥对来连接服务器。

【问题讨论】:

  • 你有X.509证书还是想在opcua客户端生成?
  • 我想在opcua客户端生成。

标签: go opc-ua


【解决方案1】:

要使用无/无以上的任何安全策略或使用用户名/密码登录,您需要拥有现有的 X.509 证书或自己生成一个。

请注意,证书分为两部分,一个公共证书和一个私钥。你需要两者。公共证书将被发送到 Kepware 服务器,而私钥将留在客户端并用于加密和解密。

endpoints, err := opcua.GetEndpoints(context.Background(), cfg.Endpoint)
if err != nil {
    return nil, fmt.Errorf("OPC GetEndpoints: %w", err)
}

policy := ua.SecurityPolicyURINone // Replace this with a constant of your security policy
mode := ua.MessageSecurityModeNone // Replace this with a constant of your security mode
ep := opcua.SelectEndpoint(endpoints, policy, mode)

c, err := generateCert() // This is where you generate the certificate
if err != nil {
    return nil, fmt.Errorf("generateCert: %w", err)
}

pk, ok := c.PrivateKey.(*rsa.PrivateKey) // This is where you set the private key
if !ok {
    return nil, fmt.Errorf("invalid private key")
}

cert := c.Certificate[0]
opts := []opcua.Option{
    opcua.SecurityPolicy(policy),
    opcua.SecurityMode(mode),
    opcua.PrivateKey(pk), 
    opcua.Certificate(cert),  // Set the certificate for the OPC UA Client
    opcua.AuthUsername(cfg.Username, cfg.Password), // Use this if you are using username and password
    opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeUserName),
    opcua.SessionTimeout(30 * time.Minute),
    opcua.AutoReconnect(true),
    opcua.ReconnectInterval(time.Second * 10),
    opcua.Lifetime(30 * time.Minute),
    opcua.RequestTimeout(3 * time.Second),
}

使用以下内容生成 X.509 证书。

func generateCert() (*tls.Certificate, error) {

    priv, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        return nil, fmt.Errorf("failed to generate private key: %s", err)
    }

    notBefore := time.Now()
    notAfter := notBefore.Add(365 * 24 * time.Hour) // 1 year

    serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
    serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
    if err != nil {
        return nil, fmt.Errorf("failed to generate serial number: %s", err)
    }

    template := x509.Certificate{
        SerialNumber: serialNumber,
        Subject: pkix.Name{
            Organization: []string{"Test Client"},
        },
        NotBefore: notBefore,
        NotAfter:  notAfter,

        KeyUsage:              x509.KeyUsageContentCommitment | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment | x509.KeyUsageCertSign,
        ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
        BasicConstraintsValid: true,
    }

    host := "urn:testing:client"
    if ip := net.ParseIP(host); ip != nil {
        template.IPAddresses = append(template.IPAddresses, ip)
    } else {
        template.DNSNames = append(template.DNSNames, host)
    }
    if uri, err := url.Parse(host); err == nil {
        template.URIs = append(template.URIs, uri)
    }

    derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
    if err != nil {
        return nil, fmt.Errorf("failed to create certificate: %s", err)
    }

    certBuf := bytes.NewBuffer(nil)
    if err := pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
        return nil, fmt.Errorf("failed to encode certificate: %s", err)
    }

    keyBuf := bytes.NewBuffer(nil)
    if err := pem.Encode(keyBuf, pemBlockForKey(priv)); err != nil {
        return nil, fmt.Errorf("failed to encode key: %s", err)
    }

    cert, err := tls.X509KeyPair(certBuf.Bytes(), keyBuf.Bytes())
    return &cert, err
}

这是一个直接来自 gopcua 项目的示例: https://github.com/gopcua/opcua/blob/main/examples/crypto/generate_cert.go

【讨论】:

  • 但它每次运行都会创建新证书,这就是 OPC UA 配置管理器添加新的受信任客户端的原因。我应该在 OPC UA 配置管理器上签署“信任”证书。当将证书签署为受信任并再次运行客户端时,它会创建新的不受信任的证书。这是一个循环。
  • 如果您已经有证书,您可以将其作为文件加载到byte[]。查看我提供链接的加密示例。
  • 好的,但是,我怎样才能获得创建的证书和私钥来再次加载它们。我需要他们的文件路径来使用“opcua.CertificateFile(证书文件路径)”或私钥。它将证书作为“.der”加载到 OPC UA Maneger,但我该如何使用它。
  • 它需要证书和私钥,我可以从 OPC UA 客户端管理器获取证书,但是如何获取私钥呢?这是我从文件加载证书时的错误:“opcua: invalid channel config: Security policy 'opcfoundation.org/UA/SecurityPolicy#Basic256Sha256' requires a private key”"
  • 您需要生成证书和私钥。有很多方法可以做到这一点。我使用 OPC 基金会提供的可执行文件来完成。上面的 Go 代码也可以。
猜你喜欢
  • 2020-07-24
  • 2020-08-28
  • 2023-02-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-08
  • 2023-04-05
  • 2020-12-23
  • 2018-09-01
相关资源
最近更新 更多