OPC UA(Unified Architecture,统一架构)是下一代的OPC 标准,它是一种工业通讯协议,通过提供一个完整的,安全和可靠的跨平台的架构,以获取实时和历史数据和时间。
OPC UA的诞生是希望为现有所有的基于COM的规范,建立一个没有损失任何功能和性能的真正替代平。此外,它必须满足能够描述复杂系统的丰富和可扩展的建模能力,以及平台独立的系统接口的所有需求。
协议白皮书链接:https://reference.opcfoundation.org/
OPC UA 数据传输
UA安全架构

机密性:加密完整性:签名应用程序认证和授权:OpenSecureChannel请求和OpenSecureChannel应答中的应用程序实例证书。

产品认证和授权:CreateSession应答和ActivateSession请求报文中的软件证书用户认证和授权:ActivateSession请求和ActivateSession应答报文。
OPC UA 连接建立过程

建立连接
客户端创建socket之后,发送Hello报文,其中包括客户端支持的buffer大小。服务器接收报文后返回Acknowledge报文完成buffer大小协商。协商好的buffer大小会上报给SecureChannel层,其中SendBufferSize字段指定在此连接上发送的MessageChunk的最大长度。


Hello/Acknowledge报文一般只能发送一次,若对端再一次接收到该报文会报错并关闭socket。如果socket创建之后,服务器很长一段时间(可配,不超过2分钟)内没有接收到Hello报文会主动关闭socket。
客户端接收到Acknowledge报文后,紧接着会发送OpenSecureChannel请求报文,服务器若接受该channel会将该socket和一个SecureChannelId关联,后续服务器发送应答报文时会根据SecureChannelId决定使用哪个socket发送。客户端接收到OpenSecureChannel应答报文后也会做相同的操作。



连接断开
客户端首先发送CloseSession请求报文准备关闭连接,服务器收到后,会回复一个CloseSessionResponse报文。

客户端发送CloseSecureChannel请求报文主动关闭连接,服务器接收该报文后会释放该channel相关的所有资源,但不会发送CloseSecureChannel应答报文。

传输协议
UA tcp
通用的opc ua tcp的消息格式如下,包含一个消息头和消息体
白皮书:https://reference.opcfoundation.org/v105/Core/docs/Part6/7.1.2/

消息头
每个 OPC UA 连接协议消息都有一个消息头,消息头包含一个消息类型的消息长度,MessageSize代表的整个消息头和消息体的总长度。
| Name | Type | Description |
| MessageType | byte [3] | 标识消息类型的三字节ascll代码 |
| Reserved | byte [1] | 如果MessageType是OPC UA连接协议支持的值之一,则应设置为' F '的ASCII码 |
| MessageSize | uint32(4byte) | Message的长度,以字节为单位。该值包括MessageType的 8 个字节 |
MessageType类型
| Name | Description |
| HEL | HELLO报文 |
| ACK | Acknowledge报文 |
| ERR | Error报文 |
消息体
Hello Message
| Name | Type | Description |
| Protocol Version | uint32 | 客户端发送请求的 UACP 协议版本。如果服务器不支持请求的版本或任何更低的版本,它会通过返回Bad_ProtocolVersionUnsupported来拒绝客户端。如果服务器支持请求的版本或更低的版本,它将在确认消息中返回它将使用的版本。此版本标准的ProtocolVersion为 0。 |
| ReceiveBufferSize | uint32 | 发送方可以接收的最大MessageChunk。如果发送方打算使用 ECC安全策略,则应至少为 1024 字节。否则,应至少为 8192 字节 |
| SendBufferSize | uint32 | 发送方可以发送的最大MessageChunk 。如果发送方打算使用 ECC安全策略,则应至少为 1024 字节。否则,应至少为 8192 字节 |
| MaxMessageSize | uint32 | 所有响应信息的最大大小,如果MessageChunks没有被发送,或者响应消息超过这个值,服务器将返回一个带有Bad_ResponseTooLarge错误的错误消息。 |
| MaxChunkCount | uint32 | 所有响应信息中的块最大数量,如果响应消息超过这个值,服务器将返回一个带有Bad_ResponseTooLarge错误的错误消息。 |
| EndpointUrl | string | 客户端希望连接的端点的URL ,编码值小于4086字节。如果长度超过4096或者它不能识别URL标识的资源,服务器将返回一个带有Bad_ResponseTooLarge错误的错误消息 |
显示详细信息
Acknowledge Message
| Name | Type | Description |
| Protocol Version | uint32(4byte) | 服务器支持的协议版本<= Hello Message请求的协议版本。如果客户端接收协议版本的,它一个确保它发送符合此版本的消息。此版本标准的ProtocolVersion为 0。 |
| ReceiveBufferSize | uint32(4byte) | 发送方可以接收的最大MessageChunk。该值不应大于Hello 消息中请求的SendBufferSize,Hello消息中请求的SendBufferSize >= 8192 字节,则该值应至少为 8192 字节。否则应至少为 1024 字节 |
| SendBufferSize | uint32(4byte) | 发送方可以发送的最大MessageChunk。该值不应大于Hello 消息中请求的ReceiveBufferSize。如果 Hello消息中请求的ReceiveBufferSize >= 8192 字节,则该值应至少为 8192 字节。否则应至少为 1024 字节 |
| MaxMessageSize | uint32(4byte) | 任何请求信息的最大大小 |
| MaxChunkCount | uint32(4byte) | 任何请求信息中的块最大数量 |
Error Message
| Name | Type | Description |
| Error | uint32(4byte) | 错误数字代码 |
| Reason | string | 详细的错误描述,字符串不得超过4096 |
客户端在收到错误信息后,应该关闭套接字连接
具体数字代码含义参见白皮书
RecverseHello Message
| Name | Type | Description |
| ServerUri | string | 发送Message的服务器 的ApplicationUri。编码值应小于4096 字节。 如果长度超过40 96 或者如果它不能识别由 URI 标识的服务器,客户端将返回一个 Bad_TcpEndpointUrlInvalid 错误并关闭连接。 |
| EndpointUrl | string | 客户端在建立SecureChannel时使用的端点的 URL 。该值应在Hello Message中传递回服务器。编码值应小于 4096 字节。 如果长度超过 4096 或无法识别 URL 标识的资源,客户端应返回 Bad_TcpEndpointUrlInvalid 错误并关闭连接。此值是服务器的唯一标识符,客户端可用于查找配置信息。它应该是GetEndpoints Service返回的 URL 之一。 |
对于基于连接的协议,例如 TCP,ReverseHello 消息允许防火墙后面没有开放端口的服务器连接到客户端,并请求客户端使用服务器创建的套接字建立SecureChannel。
对于基于消息的协议,ReverseHello 消息允许服务器向客户端宣布它们的存在。在这种情况下,EndpointUrl指定服务器的特定地址和访问它所需的任何令牌。
消息安全协议
UA secure conversation
MessageChunk 结构
OPC UA 安全会话格式如下图

消息头
每个MessageChunk都有一个Message标头
| Name | Type | Description |
| MessageType | byte[3] | 标识消息类型 |
| IsFinal | byte[1] | 指示MessageChunk是否是Message中的最后一个块,该字段仅对“MSG”的消息类型有意义。对于其他消息类型,该字段始终为“F” |
| MessageSize | uint32(4byte) | MessageChunk 的长度,以字节为单位 |
| SecureChannelId | uint32(4byte) | 服务器分配的SecureChannel 的唯一标识符 |
MessageType
| Name | Description |
| MSG | MSG 使用与通道关联的密钥保护的消息 |
| OPN | OPN OpenSecureChannel消息 |
| CLO | CLO CloseSecureChannel消息 |
IsFinal
| Name | Description |
| C | 分片时中间的chunk |
| F | 最后一个chunk |
| A | 最后一个chunk(在发生错误并且消息被中止时使用) |
安全头
消息头后跟一个安全标头,该标头指定已将哪些加密操作应用于Message。有两个版本的安全标头,这取决于应用于Message的安全类型。
非对称算法安全头
| Name | Type | Description |
| SecurityPolicyUriLength | int32(4byte) | SecurityPolicyUri 的长度(以字节为单位)。该值不得超过 255 个字节 |
| SecurityPolicyUri | byte[1] | 用于保护消息的安全策略的URI |
| SenderCertificateLength | int32(4byte) | 发件人证书长度(以字节为单位),该值不得超过MaxSenderCertificateSize字节。如果未指定证书,则此值可能为 0 或 -1。 |
| SenderCertificate | byte[1] | 分配给发送应用程序实例 的 X.509 v3证书 |
| ReceiverCertificateThumbprintLength | int32 | 接收人证书长度(以字节为单位),如果加密,该字段的值为 20 字节。如果未加密,则该值可能是 0 或 -1。 |
| ReceiverCertificateThumbprint | byte[1] | 分配给接收应用程序实例的 X.509 v3证书 的指纹 |
如果安全头中的任何字段长度无效,接收方应关闭通信通道。
对称算法安全头
| Name | Type | Description |
| TokenId | uint32(4byte) | 用于保护消息的SecureChannel SecurityToken 的唯一标识符。此标识符由服务器在OpenSecureChannel响应消息中返回。如果服务器接收到它无法识别的 TokenId,它应返回适当的传输层错误。 |
序列头
安全头后面总是跟者序列头。序列头确保通过通道发送的每条消息的第一个加密块将以不同的数据开始,序列头包含一个数字以标识数据块,它所在的消息体(如编码的服务消息)不能放进单个块,因此不得不拆分为多个块使用。
| Name | Type | Description |
| SequenceNumber | uint32(4byte) | 发送方为通过SecureChannel发送的每个MessageChunk分配的单调递增序列号。 |
| RequestId | uint32(4byte) | 客户端分配给 OPC UA 请求Message的标识符。请求和相关响应的所有MessageChunk都使用相同的标识符。 |
消息体
序列头之后是消息体,消息体使用 OPC UA 二进制编码进行编码,白皮书相关介绍,正文可以拆分为多个MessageChunks。
示例
BroweRqeuset
![]()

小结
到这里,相信你对OPC UA协议已经有所了解,在分析OPC UA 协议时,通过Wireshark抓包就能看到协议内部实现 browse 请求的细节,对照官方基金会提高的白皮书第四篇,就能理解内部数据交互的各个细节,所以只要左手Wireshark,右手OPC UA白皮书,即可轻松实现协议入门。
OPC UA协议分析与结构解析
4859

被折叠的 条评论
为什么被折叠?



