使用证书实现的Golang Opc UA客户端实现

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

Golang Opc UA Client Implementation with Certificate

问题

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

使用证书实现的Golang Opc UA客户端实现

在golang应用程序中,我使用了"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),
		}

当安全策略和模式都为"none"时,我可以无问题地连接到服务器。但是当我选择其他安全策略时,我不知道如何实现代码或SSL密钥对以连接服务器。

英文:

I am trying to connect my local Kepware OPC UA server with certificate. OPC UA server security info:
使用证书实现的Golang Opc UA客户端实现

In golang app , I use "github.com/gopcua/opcua/ua" opc client package. I create key pair and I give them path to variables and used them in config:

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

When security policy and mode are none, I can connect to the server without any problem. When I chose these security policies I don't know how to implement code or ssl key pair to connect server.

答案1

得分: 1

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

请注意,证书由两部分组成,即公共证书和私钥。您需要同时拥有这两部分。公共证书将发送到Kepware服务器,而私钥将保留在客户端并用于加密和解密。

以下是生成X.509证书的代码示例:

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

policy := ua.SecurityPolicyURINone // 将此替换为您的安全策略的常量
mode := ua.MessageSecurityModeNone // 将此替换为您的安全模式的常量
ep := opcua.SelectEndpoint(endpoints, policy, mode)

c, err := generateCert() // 在此处生成证书
if err != nil {
	return nil, fmt.Errorf("generateCert: %w", err)
}

pk, ok := c.PrivateKey.(*rsa.PrivateKey) // 在此处设置私钥
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),  // 为OPC UA客户端设置证书
	opcua.AuthUsername(cfg.Username, cfg.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年有效期

	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

英文:

In order to use any security policy above None/None or to use a username/password login, you need to either have an existing X.509 certificate or generate one yourself.

Note, the certificate comes in two parts, a public certificate and a private key. You need both. The public certificate will be sent to the Kepware server while the private key will stay with the client and be used for encryption and decryption.

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

Use the following to generate the X.509 certificate.

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
}

Here is an example directly from the gopcua project:
https://github.com/gopcua/opcua/blob/main/examples/crypto/generate_cert.go

huangapple
  • 本文由 发表于 2022年1月4日 22:26:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/70580290.html
匿名

发表评论

匿名网友

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

确定