带证书的 Golang Opc UA 客户端实现

标签 go opc-ua

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

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

    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),
        }

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

最佳答案

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

请注意,证书分为两部分:公共(public)证书和私钥。你两者都需要。公共(public)证书将发送到 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

关于带证书的 Golang Opc UA 客户端实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70580290/

相关文章:

go - 使用 GoLang Google Cloud Storage API 将插入的对象标记为公共(public)

Go 程序即使在经过很短的时间后也会永远休眠。持续时间

c# - 使用 OPC UA .NET 从 Kepserver 读取 "Channel1.Device1.Tag1"值

java - Milo OPC UA 服务器推送服务器逻辑中的值

node.js - OPC-UA开源框架

postgresql - 如何使postgres在容器的新暴露端口(不是5432)上监听?

go - 我如何检查我是否与 mqtt 代理失去联系?

go - 该程序在后台监听连接并等待用户输入

c# - 使用 Visual Studio 2017 构建 open62541 DLL

python - opc ua客户端到服务器