DDS通讯中间件——DDS安全规范
DDS通讯中间件——DDS安全规范做了十年DDS通讯中间件产品的程序员和大家分享一下对DDS这套规范的个人明确。预期本系列文章将包括以下内容陆续更新:
[*]DDS规范概述
[*]DCPS规范解读 & QoS计谋
[*]XTypes规范解读
[*]RTPS规范解读
[*]DDS安全规范解读
[*]DDS-RPC规范解读
[*]DDS-TSN规范解读
[*]DDS-XRCE规范解读
1. 概述
1.1. DDS安全特点
许多时候我们向客户先容DDS是开放式的,应用程序之间是松耦合的,发布方和吸收方甚至不需要感知到对方的存在,客户第一反应会是:“这样会不会不安全啊?要是有人乱发布/订阅怎么办?数据怎么保证安全?”。基于这些诉求,OMG组织在2016年提出了DDS-Security规范,这份规范提出了完备、细粒度的机制来保证基于DDS的分布式系统中的数据安全,这份协议完备定义了包括:身份验证、访问控制、数据加密等安全插件的功能逻辑,并提供二次开发接口,允许用户实现自定义的安全插件,而且针对常用的安全需求提供了预置的几个插件定义,实现开箱即用。DDS安全规范是DDS区别其他中间件的一个显著特点,其特征如下:
[*]应用层的安全机制,不依赖特定的底层传输协议,类似于SOME/IP假如需要数据安全传输,只能依赖底层的TLS/DTLS。
[*]细粒度的权限控制,权限的控制可以在:应用、域、主题发布、主题订阅、特定实例数据;
[*]灵活的加密控制,在雷同的信道上,针对差别的主题数据可以选择多种加密方式,包括:不加密、签名、完备加密;
[*]支持自定义加密计谋,支持二次开发适合自身场景的加密插件。
1.2. DDS安全使用场景
[*] 个人以为DDS安全规范在私有网络中(物理隔离)的使用意义不大,比方:一部雷达内部、一艘船上的指控网络,一般这种场景下不存在内部的恶意应用、换句话说假如私有网络已经被入侵了的话,在DDS上再搞DDS安全的意义不大,固然在这种场景下,可以由总体部门来设计DDS安全计谋,为每个分系统分配权限,以防止配套的分系统写错或者订阅/发布无关的主题。
[*] DDS安全规范在需要接入共有网络,尤其是无线网络场景下黑白常有必要的,比方:将来车-车协同、车-路协同中,不可避免的需要将车辆自身的一些敏感信息发布到网络中或者从路测获取一些场景信息,此时对于发送端/吸收端的身份认证就非常有必要。
1.3. DDS安全规范纲要
DDS-Security规范现在最新版本是2018年发布的1.1版本,规范相干的资料页面参考,DDS安全规范的紧张内容拜见如下的头脑导图,规范大部门内容是安全协议的实现细节,由于大部门职员不是DDS产品开发职员而是使用职员,而且大部门读者不需要自行开发自定义的安全插件而只需要使用内置插件获取开发好的插件,以是这篇重点先容内置插件的使用流程以及实例,扼要先容实现的原理,如有兴趣私信讨论。
https://i-blog.csdnimg.cn/direct/dd8f632a37f8439ba082c83e4206439c.png#pic_center
2. 需求分析
安全需求中涉及以下几个角色:
[*]张三:DDS域参与者,授权发布主题名为Demo的主题数据;
[*]李四:DDS域参与者,授权订阅主题名为Demo的主题数据;
[*]王五:一个窃听者,他没有被授权订阅Demo主题数据,但是他和其他人毗连在同一网络,试图查看数据;
[*]赵六:一个入侵者,他没有被授权发布Demo主题数据,但是他和其他人毗连在同一网络,试图发送数据;
[*]孙七:恶意的DDS域参与组合,他被授权订阅主题 Demo主题数据,但他未被授权发布主题Demo的数据。但是,孙七尝试订阅数据获得的信息并发布,并试图说李四他是正当的发布者;
[*]周八:是一个需要吸收和发送有关Demo主题数据的受信任服务。比方:持久性服务或中继服务。他被信任可以无恶意地中继信息。但不被信任可以查看信息的内容;
2.1. 未授权的订阅
王五与其他角色毗连到雷同的网络基础架构,尽管这些消息并非发送给王五,但他可以大概观察网络数据包:比方:
[*]王五可以使用DDS订阅Demo主题,在这种情况下需要张三在匹配通讯前验证对方的身份是被授权过的;
[*]王五可以观察通讯信道,比方:抓包,或者在张三和李四通过多播进行通讯的情况下,王五可以简单地侦听雷同的多播地址,在这种情况下张三需使用仅与李四、赵六等授权吸收者共享的密钥加密发送的数据。
2.2. 未授权的发布
赵六与其他角色毗连到雷同的网络基础架构,赵六可以通过以下方式注入数据:
[*]赵六恶意使用DDS发布Demo主题,在这种情况下需要李四在吸收数据之前验证对方的身份是被授权过的;
[*]赵六伪造张三的报文头,并篡改数据发送到李四的地址+端口,在这种情况下需要张三发送的数据包含基于共享密码哈希的消息认证码 (HMAC) 或数字签名;
2.2. 伪造相应
假设张三使用HMAC方式来标识本身的数据,由于孙七是正当的订阅者,以是张三会把用于HMAC的密码分享给孙七,孙七拿到这个密码之后,他可以使用这个密码来生成HMAC假装本身是张三在发送数据。这种情况下需要:
[*]细化权限,对于雷同的主题发布以及订阅的权限需要单独控制;
[*]张三给每个吸收者共享的密码需要差别;
2.3. 基础办法监听
周八是DDS通用服务,通用服务需要可以大概相识RTPS报文头以及子消息的元数据,但是不需要相识负载数据,这个需求需要可以大概分粒度加密RTPS的差别部门,比方:仅加密负载数据、加密子消息、加密整个RTPS报文。
3. DDS&RTPS扩展
3.1. 根本数据结构扩展
为支持DDS安全框架,该协议扩展了DDS以及RTPS协议,详细形貌拜见下表。
类型扩展协议说明Property_tDCPS规范(用户可见) & RTPS规范<字符串,字符串>格式的键值对用来存储数据BinaryProperty_tDCPS规范(用户可见) & RTPS规范<字符串,二进制缓冲区>格式的键值对用来存储数据DataHolderRTPS规范包括一个字符串的种别、Property_t数组以及BinaryProperty_t数组来存储各种透明数据,这个数据结构是后续全部数据结构的基础,后续其他扩展的类型都是这个数据结构的变体,要么直接是这个结构的别名,要么是这个数据结构的组合。TokenRTPS规范DataHolder的别名,在差别的安全插件之间传递的类型是差别类型的Token。 类图拜见下图。
https://i-blog.csdnimg.cn/direct/a6d5b33fdd6b4fe899777de2aff77c48.png#pic_center
3.2. QoS扩展
通过扩展实体QoS的成员用于配置DDS安全,具体的扩展如下:
QoS扩展成员说明PropertyQosPolicy新增类型包含Property_t序列以及BinaryProperty_t序列的结构体。DomainParticipantQosPropertyQosPolicy类型的property成员扩展在域参与者上的属性QoSDataWriterQosPropertyQosPolicy类型的property成员扩展在数据写者上的属性QoSDataReaderQosPropertyQosPolicy类型的property成员扩展在数据读者上的属性QoS 3.3. 内置主题扩展
为交换DDS安全相干的信息,扩展RTPS简单发现协议中定义的内置主题以及新定义其他内置主题和实体,详细形貌如下:
内置主题说明扩展DCPSParticipants复用原有的主题,扩展关联的数据类型,包括:增长身份验证令牌(Token)、权限令牌、其他QoS属性成员新增DCPSPublicationsSecure类比原有的DCPSPublication用于交换非敏感的主题信息,新增的这个主题用于交换敏感的主题信息,在原有的数据结构上扩展新增数据标签成员。新增DCPSSubscriptionsSecure类比原有的DCPSPublication用于交换非敏感的主题信息,新增的这个主题用于交换敏感的主题信息,在原有的数据结构上扩展新增数据标签成员。新增DCPSParticipantMessageSecure类比原有的DCPSParticipantMessage用于维护非敏感主题的存活性信息,新增的这个主题用于维持敏感主题的存活性信息。新增DCPSParticipantStatelessMessage用于解决“序列号攻击”,即在在执行相互认证和密钥交换之前,可能被伪造的非常大的序列号攻击导致忽略正常的序列号报文,通过在主题数据内容中添加随机序列号来解决。新增DCPSParticipantVolatileMessageSecure用于在实体之间执行密钥交换。 3.4. RTPS报文扩展
子消息扩展,结构如下图,扼要形貌如下表:
子消息说明SecureSubMsg包裹一个或者多个颠末加密、数字签名处理过的正常的RTPS子消息,与普通的Submessage结构雷同,一个消息头+多个子消息元素。SecurePrefixSubMsg加密RTPS子消息前缀,包括处理算法信息。SecurePostfixSubMsg加密RTPS子消息后缀,包括处理效果信息。SecureRTPSPrefixSubMsg加密RTPS消息前缀,包括处理算法信息。SecureRTPSPostfixSubMsg加密RTPS消息后缀,包括处理效果信息。SecureBody承载加密消息体。 https://i-blog.csdnimg.cn/direct/e70afb8acb4a4af6ac2b19d1827299a0.png#pic_center
如下所示,左边为需要传递的原始RTPS消息,右边为加密后的RTPS消息,此中第一个子消息整个都被加密,第二子消息仅负载被加密。
https://i-blog.csdnimg.cn/direct/8cc4ecaea7464dfa8cecfa7f901088eb.png#pic_center
4. 安全框架
安全框架定义了几类插件应该实现的接口,并按照一定的流程调用抽象接口从而完成插件功能,用户可以实现插件定义的接口功能实现自定义的DDS安全功能。
4.1. 身份验证插件
接口说明validate_local_identity根据参数中提供的信息验证本地区参与者的身份信息是否有效,比如:证书是否正当(伪造、过期)。validate_remote_identity根据参数中提供的信息验证发现的远程域参与者的身份的过程。begin_handshake_request当通过身份验证后,使用该操作发起握手,握手通常用于交换密钥等信息。begin_handshake_reply相应握手哀求。process_handshake处理握手。get_shaked_secret获取共享的密码。……其他获取信息以及资源回收的接口。 4.2. 访问控制插件
接口说明validate_local_permissions根据参数中提供的信息验证本地区参与者的权值配置是否有效,比如:文件是否正当(伪造、过期)。validate_remote_permissions验证远程的权限配置是否有效,如有效则生存远程的权限。check_create_*检查本地是否有权限创建“域参与者、数据写者、数据读者、主题”,假如没有则本地无法创建乐成。check_local_datawriter_*_instance检查本地是否有权限注册/注销某个实例,提供基于主题更细粒度的控制。check_remote_*检查远程是否有权限创建“域参与者、数据写者、数据读者、主题”,假如没有权限则会忽略这个实体的匹配。check_local_datawriter/reader_match基于远程实体的数据标签判断是否应该与之匹配。check_remote_datawriter_*_instance检查远程是否有权限注册/注销某个实例,提供基于主题更细粒度的控制。……其他获取信息以及资源回收的接口。 4.3. 数据加密插件
接口说明register_local_participant注册一个本地的域参与者加密句柄。register_matched_remote_participant为远端的域参与者注册一个加密句柄。register_local_datareader注册一个本地的数据读者的加密句柄。register_local_datawriter注册一个本地的数据写者的加密句柄。register_matched_remote_datareader为远端的数据读者注册一个加密句柄register_matched_remote_datawriter为远端的数据写者注册一个加密句柄unregister_*注销加密句柄。create_local_participant/datawriter/datareader_crypto_tokens创建本地区参与者/数据写者/数据读者需要传输的密钥信息。set_remote_participant/datawriter/datareader_crypto_tokens设置通过信息交换得到的远端域参与者/数据写者/数据读者密钥信息。return_crypto_tokens开释交换信息使用的空间。 5. 内置插件
为了方便用户开箱即用,DDS安全规范定义了几个内置插件,这些插件可以大概满足大部门的安全需求场景,有内置插件后用户无需按照框架规定的接口来从头实现安全插件。
5.1. DDS:Auth-PKI-DH
使用CA证书验证实体的身份并通过D-H密钥交换算法交换密码。
5.2. DDS:Access:Permissions
利用权限配置文件的形式对主题举行访问控制,配置文件由CA签名以防止伪造或者篡改。
5.3. DDS:Crypto:AES-GCM-GMAC
使用AES-GCM加密算法对差别条理的报文举行加密/签名,包括用户数据、RTPS子消息、RTPS消息。
5.4. DDS:Logging:DDS_LogTopic
通过内置DDS:Security:LogTopic主题发布日志信息,该主题的数据需颠末身份验证、签名和加密以防伪造和篡改。
6. 内置插件使用流程
这里以ZRDDS为示例展示内置插件的使用,完备的详细的示例可私信联系。
6.1. 根本概念
[*]OpenSSL 是一个安全套接字层密码库,席卷紧张的密码算法、常用密钥、证书封装管理功能及实现ssl协议。OpenSSL整个软件包大概可以分成三个紧张的功能部门:SSL协议库libssl、应用程序下令工具以及密码算法库libcrypto。
[*].KEY 通常指私钥。
[*].CSR 是 Certificate Signing Request 的缩写,即证书签名哀求,这不是证书,只是包含申请证书的根本信息。生成证书时要把这个提交给权威的证书颁发机构,颁发机构审核通过之后,再根据这些申请信息生成相应的证书。
[*].CRT 即 certificate的缩写,即证书。
[*]X.509 是一种证书格式。对X.509证书来说,认证者总是CA或由CA指定的人,一份X.509证书是一些尺度字段的集合,这些字段包含有关用户或设备及其相应公钥的信息。
[*]X.509的证书文件,一般以.crt末端,根据该文件的内容编码格式,可以分为以下二种格式:
[*].PEM - Privacy Enhanced Mail,打开看文本格式,以"-----BEGIN…“开头,”-----END…"末端,内容是 BASE64 编码。Apache 和 *NIX 服务器方向于使用这种编码格式。
[*].DER - Distinguished Encoding Rules,打开看是二进制格式,不可读。Java 和 Windows 服务器方向于使用这种编码格式。
6.2. 配置CA
[*]切换到将要存储CA的目录,创建CA工作目录并进入,比方cd ~ && mkdir demoCA && cd demoCA,demoCA可更换为其他名称;
[*]拷贝openssl配置文件到demoCA目录,Windows为Openssl安装目录/bin/openssl.cnf,Linux上为/etc/ssl/openssl.cnf,修改并确认文件内容;
dir = . # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem# The private key
RANDFILE = $dir/private/.rand # private random number file
default_days= 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md= md5 # which message digest to use
preserve= no # keep passed DN ordering
[*] 使用下令mkdir ca && mkdir user && mkdir newcerts && touch index.txt && touch index.txt.attr && touch serial && echo 01 >> serial创建CA所需文件或者目录结构;
[*]ca目录存储ca的证书私钥等;
[*]user目录存储用户证书哀求文件等;
[*]newcerts中存储为用户生成的证书;
[*] 创建私钥:openssl genrsa -out ca/democa.key 2048
[*]genrsa表示生成rsa算法生成密钥的子下令;
[*][可选] -des3,为私钥文件设置密码,后续使用密钥时需要密码验证;
[*]-out指定生成的私钥文件名称;
[*]2048表示加密长度,建议2048
[*] 生成证书哀求文件(CSR):openssl req -new -key ca/democa.key -subj "/C=CN/ST=JS/L=NJ/O=ZR/CN=demoCA" -out ca/democa.csr
[*]req表示产生证书签发申请子下令;
[*]-new 新的证书申请;
[*]-signkey 签名密钥(key)文件,由第一步生成。
[*]-out 输出为 CSR 文件,这是一个哀求文件。
[*]-subj 主体名称,包括以下信息:
[*]C 国家
[*]ST 省份
[*]L 市
[*]O 机构
[*]OU 部门
[*]CN (Common Name) 一般是域名
[*]EmailAddress 邮箱
[*] 自签署证书(正常申请证书是你把上面生成的哀求文件(.CSR)发送给可信机构(CA),让可信机构根据你的哀求去生成和签署证书,再给你发回来。这里是CA自签署。):openssl x509 -req -sha1 -in ca/democa.csr -signkey ca/democa.key -days 3650 -out ca/democa.pem
[*]x509为签发X.509格式证书子下令;
[*]-req 证书输入哀求;
[*]-days 证书有效天数;
[*]-in 输入文件,这里是上一步生成的哀求文件(.CSR);
[*]-signkey 签名密钥(key)文件,由第一步生成;
[*]-out 输出文件,生成证书文件(.pem)。
6.3. 签发证书
为需要使用安全的应用签发证书。
[*]以下下令可以在需要申请证书的节点上运行:
[*]创建私钥:openssl genrsa -out user/app1.key 2048
[*]生成证书哀求文件:openssl req -new -key user/app1.key -subj "/C=CN/ST=JS/O=ZR/CN=app1" -out user/app1.csr
[*]-subj标识证书的主体,后续在配置文件中会需要使用,注意:由于实现原因,ZRDDS内置插件主体名称不使用/L字段。
[*]在CA目录运行以下下令为app1颁发证书:
[*]使用根证书签发应用的证书:openssl ca -cert ./ca/democa.pem -keyfile ./ca/democa.key -config ./openssl.cnf -in user/app1.csr -out user/app1Cert.pem
6.4. 编写DDS安全相干的配置文件
6.4.1. 权限配置文件
权限配置文件紧张用于形貌本地的访问控制权限,格式为XML,根节点标签名称为dds,类型为PermissionNode,其Schema结构如下。
类型属性/子元素说明PermissionsNodepermissions出现一次的子元素,类型为PermissionsPermissionsgrant出现一次及以上的子元素,类型为GrantGrantname出现一次的属性,用于标识该规则^subject_name出现一次的子元素,指定该条配置针对的对象,subject_name是申请证书的主体,通常每个应用或者每个域参与者唯一标识;^validity出现一次的子元素,类型为Validity,表明该配置的有效期范围,当前时间不在有效期范围内的规则将被忽略。^allow_rule出现0到多次的子元素,类型为Rule,表示一组允许访问规则。^deny_rule出现0到多次的子元素,类型为Rule,表示一组拒绝访问规则。^default出现1次的子元素,类型为枚举字符串,DENY表示拒绝,ALLOW表示允许,表示未在allow_rule以及deny_rule中定义的域号、主题、分区等的默认行为。Validitynot_before出现一次的子元素,用于指定开始时间,形式为YYYY-MM-DDThh:mm::ss。^not_after出现一次的子元素,用于指定结束时间,格式与not_before雷同。RuleDomainIdSet出现一次的子元素,用于表明规则中的域号集合。^publish出现0到多次的子元素,类型为Criteria,用于形貌发布相干的权限。^subscribe出现0到多次的子元素,类型为Criteria,用于形貌订阅相干的权限。^relay出现0到多次的子元素,类型为Criteria,用于形貌发布订阅双向相干的权限。DomainIdSetid出现0到多次的子元素,类型为DomainId,每个元素代表集合中的一个域号。^id_range出现0到多次的子元素,类型为DomainIdRange,每个元素代表一个连续的域号区间。DomainIdRangemin出现一次的子元素,代表区间下界。^max出现一次的子元素,代表区间上界。DomainId无非负整数表示域号。Criteriatopics出现一次的子元素,类型为TopicExpressionList,用于指定主题列表,取决于上层规则,表示允许/拒绝该主题列表。^partitions出现一次的子元素,类型为PartitionExpressionList,用于指定分区列表,取决于上层规则,表示允许/拒绝加入这些分区列表。TopicExpressionListtopic出现1次到多次的子元素,类型为TopicExpression。TopicExpression无主题名的表达式字符串,支持POSIX 1003.2-1992的正则表达式。PartitionExpressionListpartition出现1次到多次的子元素,类型为PartitionExpression。PartitionExpression无分区名的表达式字符串,支持POSIX 1003.2-1992的正则表达式。 6.4.2. 管理配置文件
管理配置文件用于形貌怎样处理与远程实体的发现以及数据加密配置,格式为XML,根节点标签名称为dds,类型为DomainAccessRulesNode,其Schema格式如下:
类型属性/子元素说明DomainAccessRulesNodedomain_access_rules出现一次的子元素,类型为DomainAccessRulesDomainAccessRulesdomain_rule出现一次及以上的子元素,类型为DomainRuleDomainRuledomains出现一次及以上的子元素,类型为DomainIdSet,用于形貌该规则实用的域集合,该类型的形貌拜见权限配置文件形貌。^allow_unauthenticated_participants出现一次的子元素,表明是否允许匹配未授权的域参与者,true表示允许,false表示不允许。^enable_join_access_control出现一次的子元素,表明在验证完成后,是否需要进一步检查远程域参与者的权限,true表示需要检查,false表示不需要检查。^discovery_protection_kind出现一次的子元素,枚举类型表示内置发现信息的掩护级别,ENCRYPT表示加密、SIGN表示签名、NONE表示不额外掩护。^liveliness_protection_kind出现一次的子元素,枚举类型表示内置心跳发现信息的掩护级别,ENCRYPT表示加密、SIGN表示签名、NONE表示不额外掩护。^rtps_protection_kind出现一次的子元素,枚举类型表示RTPS整个消息的掩护级别,ENCRYPT表示加密、SIGN表示签名、NONE表示不额外掩护。^topic_access_rules出现一次的子元素,类型为TopicAccessRules。TopicAccessRulestopic_rule出现一次及以上的子元素,类型为TopicRule,每个元素针对一个或一组主题名的规则。TopicRuletopic_expression出现一次的子元素,主题名的表达式字符串,支持POSIX 1003.2-1992的正则表达式。^enable_discovery_protection针对该主题是否启动内置信息掩护。^enable_read_access_control出现一次的子元素,表示是否需要检查权限配置文件中的订阅权限配置。^enable_write_access_control出现一次的子元素,表示是否需要检查权限配置文件中的发布权限配置。^metadata_protection_kind出现一次的子元素,枚举类型表示主题相干RTPS子消息的掩护级别,ENCRYPT表示加密、SIGN表示签名、NONE表示不额外掩护。^data_protection_kind出现一次的子元素,枚举类型表示主题负载数据掩护级别,ENCRYPT表示加密、SIGN表示签名、NONE表示不额外掩护。 6.5. 配置文件签名
为保证配置文件的不可篡改性,需使用CA对配置文件举行签名。
[*]签名权限配置文件为permission.xml: openssl smime -sign -signer ca/democa.pem -inkey ca/democa.key -in permission.xml -out permission.smime
[*]-sign表示签名;
[*]-signer表示用于签名的证书;
[*]-inkey表示与签名证书匹配的私钥;
[*]-in表示需要签名的文件路径;
[*]-out表示签名后的文件路径;
[*]签名管理配置文件为governance.xml: openssl smime -sign -signer ca/democa.pem -inkey ca/democa.key -in governance.xml -out governance.smime
6.6. 配置QoS并加载安全插件
安全插件表现形式为一个动态库,动态库必须提供获取/删除实例的函数,使用 DDS::DomainParticipantFactory::load_security_plugin 函数加载安全插件。
在使用安全插件时,通过 DomainparticipantQos.property对指定插件举行参数配置,内置插件配置项如下,此中属性的值支持两种形式,file前缀表示文件路径,data前缀表示文件内容:
配置键名称说明配置值(file示例)配置值(data示例)dds.sec.auth.certificate_revocation_list[可选],由证书机构提供的失效证书列表。在列表中的证书纵然未到有效期,也会被以为黑白法证书。file: client.crldata:, -----BEGIN X509 CRL-----
MIIBazCB1QIBATANBgkqhki … dFo/Zs14keJ06Oug==
-----END X509 CRL-----dds.sec.auth.identity_ca用于身份验证的CA证书。file:ca.pemdata:,-----BEGIN CERTIFICATE-----
MIIC3DCCAcQCCQCWE5x+Z … PhovK0mp2ohhRLYI0ZiyYQ==
-----END CERTIFICATE-----dds.sec.auth.identity_certificate标识本身身份的证书,由CA颁发。file:app1Cert.pemdata:,-----BEGIN CERTIFICATE-----
MIIDjjCCAnYCCQDCEu9…6rmT87dhTo=
-----END CERTIFICATE-----dds.sec.auth.private_key与自身证书匹配的自身私钥。file:app1.keydata:,-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA3HIh…AOBaaqSV37XBUJg==
-----END RSA PRIVATE KEY-----dds.sec.access.permissions_ca用于访问控制的CA证书(可以与身份验证CA证书雷同)。file:ca.pem略dds.sec.access.permissions使用访问控制CA签名的许可配置文件file:permission.smime略dds.sec.access.governance使用访问控制CA签名的管理配置文件file:governance.smime略 7. 内置插件使用实例
7.1. QoS配置文件
三个内置插件所涉及全部QoS配置放在以下的zrdds_security_qos.xml文件中。
<zrdds_qos xmlns="http://www.omg.org/dds/">
<qos_library name="default_lib">
<qos_profile name="default_profile">
<participantfactory_qos name="security_example">
<property>
<value />
</property>
</participantfactory_qos>
<participant_qos name="auth_example_pub">
<property>
<value>
<element>
<name>dds.sec.plugin_name</name>
<value>BuiltinPlugin</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_certificate</name>
<value>file:./config/app1Cert.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.private_key</name>
<value>file:./config/app1.key</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions</name>
<value>file:./config/auth_permission.smime</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.governance</name>
<value>file:./config/auth_governance.smime</value>
<propagate>false</propagate>
</element>
</value>
</property>
</participant_qos>
<participant_qos name="auth_example_sub_auth">
<property>
<value>
<element>
<name>dds.sec.plugin_name</name>
<value>BuiltinPlugin</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_certificate</name>
<value>file:./config/app2Cert.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.private_key</name>
<value>file:./config/app2.key</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions</name>
<value>file:./config/auth_permission.smime</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.governance</name>
<value>file:./config/auth_governance.smime</value>
<propagate>false</propagate>
</element>
</value>
</property>
</participant_qos>
<participant_qos name="auth_example_sub_not_auth" />
<participant_qos name="ac_example_pub">
<property>
<value>
<element>
<name>dds.sec.plugin_name</name>
<value>BuiltinPlugin</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_certificate</name>
<value>file:./config/app1Cert.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.private_key</name>
<value>file:./config/app1.key</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions</name>
<value>file:./config/ac_permission.smime</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.governance</name>
<value>file:./config/ac_governance.smime</value>
<propagate>false</propagate>
</element>
</value>
</property>
</participant_qos>
<participant_qos name="ac_example_sub">
<property>
<value>
<element>
<name>dds.sec.plugin_name</name>
<value>BuiltinPlugin</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_certificate</name>
<value>file:./config/app2Cert.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.private_key</name>
<value>file:./config/app2.key</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions</name>
<value>file:./config/ac_permission.smime</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.governance</name>
<value>file:./config/ac_governance.smime</value>
<propagate>false</propagate>
</element>
</value>
</property>
</participant_qos>
<participant_qos name="encry_example_pub">
<property>
<value>
<element>
<name>dds.sec.plugin_name</name>
<value>BuiltinPlugin</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_certificate</name>
<value>file:./config/app1Cert.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.private_key</name>
<value>file:./config/app1.key</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions</name>
<value>file:./config/encry_permission.smime</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.governance</name>
<value>file:./config/encry_governance.smime</value>
<propagate>false</propagate>
</element>
</value>
</property>
</participant_qos>
<participant_qos name="encry_example_sub">
<property>
<value>
<element>
<name>dds.sec.plugin_name</name>
<value>BuiltinPlugin</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.identity_certificate</name>
<value>file:./config/app2Cert.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.auth.private_key</name>
<value>file:./config/app2.key</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions_ca</name>
<value>file:./config/democa.pem</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.permissions</name>
<value>file:./config/encry_permission.smime</value>
<propagate>false</propagate>
</element>
<element>
<name>dds.sec.access.governance</name>
<value>file:./config/encry_governance.smime</value>
<propagate>false</propagate>
</element>
</value>
</property>
</participant_qos>
<datawriter_qos name="reliable">
<reliability>
<kind>RELIABLE_RELIABILITY_QOS</kind>
</reliability>
</datawriter_qos>
<datareader_qos name="reliable">
<reliability>
<kind>RELIABLE_RELIABILITY_QOS</kind>
</reliability>
</datareader_qos>
</qos_profile>
</qos_library>
</zrdds_qos>
7.2. 身份验证插件
auth_example_pub中的域参与者配置了身份验证,且不允许未授权的域参与者匹配,发布主题auth_example,auth_example_sub 中有两个域参与者,此中一个配置了证书、另一个未配置证书的普通域参与者,订阅雷同的主题,可以看到吸收端只能订阅到一份数据。
7.2.1. 管理配置文件
<?xml version="1.0" encoding="UTF-8"?>
<dds>
<domain_access_rules>
<domain_rule>
<domains>
<id>150</id>
</domains>
<!-- 设置不允许未认证身份的域参与者加入域。 -->
<allow_unauthenticated_participants>FALSE</allow_unauthenticated_participants>
<enable_join_access_control>FALSE</enable_join_access_control>
<discovery_protection_kind>NONE</discovery_protection_kind>
<liveliness_protection_kind>NONE</liveliness_protection_kind>
<rtps_protection_kind>NONE</rtps_protection_kind>
<topic_access_rules>
<topic_rule>
<topic_expression>*</topic_expression>
<enable_discovery_protection>FALSE</enable_discovery_protection>
<enable_read_access_control>FALSE</enable_read_access_control>
<enable_write_access_control>FALSE</enable_write_access_control>
<metadata_protection_kind>NONE</metadata_protection_kind>
<data_protection_kind>NONE</data_protection_kind>
</topic_rule>
</topic_access_rules>
</domain_rule>
</domain_access_rules>
</dds>
7.2.2. 权限管理文件
<?xml version="1.0" encoding="UTF-8"?>
<dds>
<permissions>
<!-- 设置app1的权限,默认允许所有主题发布订阅。 -->
<grant name="app1">
<subject_name>C=CN,ST=JS,O=ZR,CN=app1</subject_name>
<validity>
<not_before>2020-10-19T10:54:00</not_before>
<not_after>2022-10-19T10:54:00</not_after>
</validity>
<default>ALLOW</default>
</grant>
<!-- 设置app2的权限,默认允许所有主题发布订阅。 -->
<grant name="app2">
<subject_name>C=CN,ST=JS,O=ZR,CN=app2</subject_name>
<validity>
<not_before>2020-10-19T10:54:00</not_before>
<not_after>2022-10-19T10:54:00</not_after>
</validity>
<default>ALLOW</default>
</grant>
</permissions>
</dds>
7.2.3. 发布端代码
注:这里为了减少代码量,使用简化接口,使用尺度DCPS接口类似。
#include "ZRDDSCppSimpleInterface.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace DDS;
int pub(int argc, char* argv[])
{
// 获取入口,并指明QoS配置
DomainParticipantFactory* factory = DDSIF::Init("./config/zrdds_security_qos.xml", "security_example");
if (factory == NULL) { printf("DDSIF::Initialize failed.\n"); return -1; }
// 加载内置插件
ReturnCode_t retCode = factory->load_security_plugin(
"BuiltinPlugin",
"ZRDDSSecBuiltinPlugin.dll",
"BuiltinSecPluginGetInstance",
"BuiltinSecPluginFinalize");
if (retCode != NULL)
{
printf("load_security_plugin failed(%d).", retCode);
return -1;
}
// 初始化使用rio的域参与者
DomainParticipant* udpDp = DDSIF::CreateDP(150, "auth_example_pub");
if (NULL == udpDp) { printf("DDSIF::CreateDP failed.\n"); return -2; }
// 创建数据写者
DataWriter* rawWriter = DDSIF::PubTopic(
udpDp, "auth_example", BytesTypeSupport::get_instance(), "reliable", NULL);
BytesDataWriter* writer = (BytesDataWriter*)rawWriter;
// 发送数据
Bytes sample;
sample.value.ensure_length(1024, 1024);
unsigned int counter = 0;
while (++counter < 100)
{
sprintf((char*)sample.value._contiguousBuffer, "sample(%u) from auth_example_pub.", counter);
ReturnCode_t retCode = writer->write(sample, HANDLE_NIL_NATIVE);
if (retCode != RETCODE_OK)
{
printf("send failed(%d).\n", retCode);
}
printf("send sample %s\n", sample.value._contiguousBuffer);
ZRSleep(1000);
}
return 0;
}
int main(int argc, char* argv[])
{
return pub(argc, argv);
}
7.2.4. 订阅端代码
注:这里为了减少代码量,使用简化接口,使用尺度DCPS接口类似。
#include "ZRDDSCppSimpleInterface.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace DDS;
// 定义零拷贝数据类型回调接口
class BytesTypeListener :
virtual public SimpleDataReaderListener<Bytes, BytesSeq, BytesDataReader>
{
public:
virtual void on_process_sample(DataReader* reader, const Bytes& sample, const SampleInfo& info)
{
// 通过该语句获取收到的样本所属主题名称
const char* topicName = reader->get_topicdescription()->get_name();
// TODO 在此填写业务处理,接收端sample.userBuffer为用户负载数据,sample.userLength为用户负载长度,无需考虑预留空间
printf("received data(%s) from topic(%s)\n",
sample.value.get_contiguous_buffer(),
topicName);
}
};
int sub()
{
// 获取入口,并指明QoS配置
DomainParticipantFactory* factory = DDSIF::Init("./config/zrdds_security_qos.xml", "security_example");
if (factory == NULL) { printf("DDSIF::Initialize failed.\n"); return -1; }
// 加载内置插件
ReturnCode_t retCode = factory->load_security_plugin(
"BuiltinPlugin",
"ZRDDSSecBuiltinPlugin.dll",
"BuiltinSecPluginGetInstance",
"BuiltinSecPluginFinalize");
if (retCode != NULL)
{
printf("load_security_plugin failed(%d).", retCode);
return -1;
}
// 初始化授权的域参与者
DomainParticipant* authDP = DDSIF::CreateDP(150, "auth_example_sub_auth");
if (NULL == authDP) { printf("DDSIF::CreateDP failed.\n"); return -2; }
// 创建数据读者,并设置监听器
BytesTypeListener* m_listener = new BytesTypeListener();
DataReader* authDr = DDSIF::SubTopic(
authDP, "auth_example", BytesTypeSupport::get_instance(), "reliable", m_listener);
if (NULL == authDr) { printf("DDSIF::SubscribeTopic failed.\n"); return -5; }
// 初始化未授权的域参与者
DomainParticipant* unAuthDP = DDSIF::CreateDP(150, "auth_example_sub_not_auth");
if (NULL == unAuthDP) { printf("DDSIF::CreateDP failed.\n"); return -2; }
// 创建数据读者,并设置监听器
//BytesTypeListener* m_listener = new BytesTypeListener();
DataReader* noAuthDr = DDSIF::SubTopic(
unAuthDP, "auth_example", BytesTypeSupport::get_instance(), "reliable", m_listener);
if (NULL == noAuthDr) { printf("DDSIF::SubscribeTopic failed.\n"); return -5; }
getchar();
DDSIF::Finalize();
return 0;
}
int main(int argc, char* argv[])
{
return sub();
}
7.3. 访问控制插件
ac_example_pub中发布两个主题ac_example_1、ac_example_2,权值配置文件中配置允许发布ac_example_开头的主题,ac_example_sub中订阅两个主题,权限配置文件中配置仅允许订阅ac_example_1主题,可以看到只能乐成创建ac_example_1的订阅。
7.3.1. 管理配置文件
<?xml version="1.0" encoding="UTF-8"?>
<dds>
<domain_access_rules>
<domain_rule>
<domains>
<id>150</id>
</domains>
<allow_unauthenticated_participants>FALSE</allow_unauthenticated_participants>
<enable_join_access_control>FALSE</enable_join_access_control>
<discovery_protection_kind>NONE</discovery_protection_kind>
<liveliness_protection_kind>NONE</liveliness_protection_kind>
<rtps_protection_kind>NONE</rtps_protection_kind>
<topic_access_rules>
<topic_rule>
<!-- 设置ac_example_*主题 需要订阅/发布权限保护 -->
<topic_expression>ac_example_*</topic_expression>
<enable_discovery_protection>FALSE</enable_discovery_protection>
<enable_read_access_control>TRUE</enable_read_access_control>
<enable_write_access_control>TRUE</enable_write_access_control>
<metadata_protection_kind>NONE</metadata_protection_kind>
<data_protection_kind>NONE</data_protection_kind>
</topic_rule>
</topic_access_rules>
</domain_rule>
</domain_access_rules>
</dds>
7.3.2. 权限管理文件
<?xml version="1.0" encoding="UTF-8"?>
<dds>
<permissions>
<!-- 设置app1只允许在150号域内发布任意以ac_example开头的主题。 -->
<grant name="app1">
<subject_name>C=CN,ST=JS,O=ZR,CN=app1</subject_name>
<validity>
<not_before>2020-10-19T10:54:00</not_before>
<not_after>2022-10-19T10:54:00</not_after>
</validity>
<allow_rule>
<domains>
<id>150</id>
</domains>
<publish>
<topics>
<topic>ac_example*</topic>
</topics>
</publish>
</allow_rule>
<default>DENY</default>
</grant>
<!-- 设置app2只允许在150号域内订阅任意以ac_example_1主题。 -->
<grant name="app2">
<subject_name>C=CN,ST=JS,O=ZR,CN=app2</subject_name>
<validity>
<not_before>2020-10-19T10:54:00</not_before>
<not_after>2022-10-19T10:54:00</not_after>
</validity>
<allow_rule>
<domains>
<id>150</id>
</domains>
<subscribe>
<topics>
<topic>ac_example_1</topic>
</topics>
</subscribe>
</allow_rule>
<default>DENY</default>
</grant>
</permissions>
</dds>
7.3.3. 发布端代码
注:这里为了减少代码量,使用简化接口,使用尺度DCPS接口类似。
#include "ZRDDSCppSimpleInterface.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace DDS;
int pub(int argc, char* argv[])
{
// 获取入口,并指明QoS配置
DomainParticipantFactory* factory = DDSIF::Init("./config/zrdds_security_qos.xml", "security_example");
if (factory == NULL) { printf("DDSIF::Initialize failed.\n"); return -1; }
// 加载内置插件
ReturnCode_t retCode = factory->load_security_plugin(
"BuiltinPlugin",
"ZRDDSSecBuiltinPlugin.dll",
"BuiltinSecPluginGetInstance",
"BuiltinSecPluginFinalize");
if (retCode != NULL)
{
printf("load_security_plugin failed(%d).", retCode);
return -1;
}
// 初始化授权的域参与者
DomainParticipant* udpDp = DDSIF::CreateDP(150, "ac_example_pub");
if (NULL == udpDp) { printf("DDSIF::CreateDP failed.\n"); return -2; }
// 创建数据写者
DataWriter* rawWriter1 = DDSIF::PubTopic(
udpDp, "ac_example_1", BytesTypeSupport::get_instance(), "reliable", NULL);
BytesDataWriter* acWriter1 = (BytesDataWriter*)rawWriter1;
if (NULL == acWriter1) { printf("DDSIF::PubTopic failed.\n"); return -5; }
DataWriter* rawWriter2 = DDSIF::PubTopic(
udpDp, "ac_example_2", BytesTypeSupport::get_instance(), "reliable", NULL);
BytesDataWriter* acWriter2 = (BytesDataWriter*)rawWriter2;
if (NULL == acWriter2) { printf("DDSIF::PubTopic failed.\n"); return -5; }
// 发送数据
Bytes sample;
sample.value.ensure_length(1024, 1024);
unsigned int counter = 0;
while (++counter < 100)
{
// 发送ac_example_1主题数据
sprintf((char*)sample.value._contiguousBuffer, "sample(%u) from %s.", counter, acWriter1->get_topic()->get_name());
ReturnCode_t retCode = acWriter1->write(sample, HANDLE_NIL_NATIVE);
if (retCode != RETCODE_OK)
{
printf("send failed(%d).\n", retCode);
}
printf("send sample %s\n", sample.value._contiguousBuffer);
// 发送ac_example_2主题数据
sprintf((char*)sample.value._contiguousBuffer, "sample(%u) from %s.", counter, acWriter2->get_topic()->get_name());
retCode = acWriter2->write(sample, HANDLE_NIL_NATIVE);
if (retCode != RETCODE_OK)
{
printf("send failed(%d).\n", retCode);
}
printf("send sample %s\n", sample.value._contiguousBuffer);
ZRSleep(1000);
}
return 0;
}
int main(int argc, char* argv[])
{
return pub(argc, argv);
}
7.3.4. 订阅端代码
注:这里为了减少代码量,使用简化接口,使用尺度DCPS接口类似。
#include "ZRDDSCppSimpleInterface.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace DDS;
// 定义零拷贝数据类型回调接口
class BytesTypeListener :
virtual public SimpleDataReaderListener<Bytes, BytesSeq, BytesDataReader>
{
public:
virtual void on_process_sample(DataReader* reader, const Bytes& sample, const SampleInfo& info)
{
// 通过该语句获取收到的样本所属主题名称
const char* topicName = reader->get_topicdescription()->get_name();
// TODO 在此填写业务处理,接收端sample.userBuffer为用户负载数据,sample.userLength为用户负载长度,无需考虑预留空间
printf("received data(%s) from topic(%s)\n",
sample.value.get_contiguous_buffer(),
topicName);
}
};
int sub()
{
// 获取入口,并指明QoS配置
DomainParticipantFactory* factory = DDSIF::Init("./config/zrdds_security_qos.xml", "security_example");
if (factory == NULL) { printf("DDSIF::Initialize failed.\n"); return -1; }
// 加载内置插件
ReturnCode_t retCode = factory->load_security_plugin(
"BuiltinPlugin",
"ZRDDSSecBuiltinPlugin.dll",
"BuiltinSecPluginGetInstance",
"BuiltinSecPluginFinalize");
if (retCode != NULL)
{
printf("load_security_plugin failed(%d).", retCode);
return -1;
}
// 初始化授权的域参与者
DomainParticipant* authDP = DDSIF::CreateDP(150, "ac_example_sub");
if (NULL == authDP) { printf("DDSIF::CreateDP failed.\n"); return -2; }
// 创建ac_example_1数据读者,并设置监听器,期望成功
BytesTypeListener* m_listener = new BytesTypeListener();
DataReader* authDr = DDSIF::SubTopic(
authDP, "ac_example_1", BytesTypeSupport::get_instance(), "reliable", m_listener);
if (NULL == authDr) { printf("DDSIF::SubscribeTopic failed.\n"); return -5; }
// 创建ac_example_2数据读者,并设置监听器,由于权限配置文件中未指明能够订阅该主题,故而订阅失败
authDr = DDSIF::SubTopic(
authDP, "ac_example_2", BytesTypeSupport::get_instance(), "reliable", m_listener);
if (NULL == authDr) { printf("DDSIF::SubscribeTopic ac_example_2 failed.\n"); }
getchar();
DDSIF::Finalize();
return 0;
}
int main(int argc, char* argv[])
{
return sub();
}
7.4. 数据加密插件
encry_example_pub发布encry_example_1和encry_example_2主题,在管理配置文件中配置主题encry_example_1掩护级别用户负载加密,encry_example_2掩护级别为空,encry_example_sub订阅对应的两个主题,使用雷同的管理配置,通过抓包可以看到encry_example_1主题数据通过密文传输,encry_example_2主题数据通过明文传输。
7.4.1. 管理配置文件
<?xml version="1.0" encoding="UTF-8"?>
<dds>
<domain_access_rules>
<domain_rule>
<domains>
<id>150</id>
</domains>
<allow_unauthenticated_participants>FALSE</allow_unauthenticated_participants>
<enable_join_access_control>FALSE</enable_join_access_control>
<discovery_protection_kind>NONE</discovery_protection_kind>
<liveliness_protection_kind>NONE</liveliness_protection_kind>
<rtps_protection_kind>NONE</rtps_protection_kind>
<topic_access_rules>
<topic_rule>
<topic_expression>encry_example_1</topic_expression>
<enable_discovery_protection>FALSE</enable_discovery_protection>
<enable_read_access_control>FALSE</enable_read_access_control>
<enable_write_access_control>FALSE</enable_write_access_control>
<metadata_protection_kind>NONE</metadata_protection_kind>
<!-- 设置encry_example_1主题需要主题数据加密传输 -->
<data_protection_kind>ENCRYPT</data_protection_kind>
</topic_rule>
<topic_rule>
<topic_expression>encry_example_2</topic_expression>
<enable_discovery_protection>FALSE</enable_discovery_protection>
<enable_read_access_control>FALSE</enable_read_access_control>
<enable_write_access_control>FALSE</enable_write_access_control>
<metadata_protection_kind>NONE</metadata_protection_kind>
<data_protection_kind>NONE</data_protection_kind>
</topic_rule>
</topic_access_rules>
</domain_rule>
</domain_access_rules>
</dds>
7.4.2. 权限管理文件
<?xml version="1.0" encoding="UTF-8"?>
<dds>
<permissions>
<grant name="app1">
<subject_name>C=CN,ST=JS,O=ZR,CN=app1</subject_name>
<validity>
<not_before>2020-10-19T10:54:00</not_before>
<not_after>2022-10-19T10:54:00</not_after>
</validity>
<default>ALLOW</default>
</grant>
<grant name="app2">
<subject_name>C=CN,ST=JS,O=ZR,CN=app2</subject_name>
<validity>
<not_before>2020-10-19T10:54:00</not_before>
<not_after>2022-10-19T10:54:00</not_after>
</validity>
<default>ALLOW</default>
</grant>
</permissions>
</dds>
7.4.3. 发布端代码
注:这里为了减少代码量,使用简化接口,使用尺度DCPS接口类似。
#include "ZRDDSCppSimpleInterface.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace DDS;
int pub(int argc, char* argv[])
{
// 获取入口,并指明QoS配置
DomainParticipantFactory* factory = DDSIF::Init("./config/zrdds_security_qos.xml", "security_example");
if (factory == NULL) { printf("DDSIF::Initialize failed.\n"); return -1; }
// 加载内置插件
ReturnCode_t retCode = factory->load_security_plugin(
"BuiltinPlugin",
"ZRDDSSecBuiltinPlugin.dll",
"BuiltinSecPluginGetInstance",
"BuiltinSecPluginFinalize");
if (retCode != NULL)
{
printf("load_security_plugin failed(%d).", retCode);
return -1;
}
// 初始化授权的域参与者
DomainParticipant* udpDp = DDSIF::CreateDP(150, "encry_example_pub");
if (NULL == udpDp) { printf("DDSIF::CreateDP failed.\n"); return -2; }
// 创建数据写者
DataWriter* rawWriter1 = DDSIF::PubTopic(
udpDp, "encry_example_1", BytesTypeSupport::get_instance(), "reliable", NULL);
BytesDataWriter* acWriter1 = (BytesDataWriter*)rawWriter1;
if (NULL == acWriter1) { printf("DDSIF::PubTopic failed.\n"); return -5; }
DataWriter* rawWriter2 = DDSIF::PubTopic(
udpDp, "encry_example_2", BytesTypeSupport::get_instance(), "reliable", NULL);
BytesDataWriter* acWriter2 = (BytesDataWriter*)rawWriter2;
if (NULL == acWriter2) { printf("DDSIF::PubTopic failed.\n"); return -5; }
// 发送数据
Bytes sample;
sample.value.ensure_length(1024, 1024);
unsigned int counter = 0;
while (++counter < 100)
{
// 发送encry_example_1主题数据
sprintf((char*)sample.value._contiguousBuffer, "sample(%u) from %s.", counter, acWriter1->get_topic()->get_name());
ReturnCode_t retCode = acWriter1->write(sample, HANDLE_NIL_NATIVE);
if (retCode != RETCODE_OK)
{
printf("send failed(%d).\n", retCode);
}
printf("send sample %s\n", sample.value._contiguousBuffer);
// 发送encry_example_2主题数据
sprintf((char*)sample.value._contiguousBuffer, "sample(%u) from %s.", counter, acWriter2->get_topic()->get_name());
retCode = acWriter2->write(sample, HANDLE_NIL_NATIVE);
if (retCode != RETCODE_OK)
{
printf("send failed(%d).\n", retCode);
}
printf("send sample %s\n", sample.value._contiguousBuffer);
ZRSleep(1000);
}
return 0;
}
int main(int argc, char* argv[])
{
return pub(argc, argv);
}
7.4.4. 订阅端代码
注:这里为了减少代码量,使用简化接口,使用尺度DCPS接口类似。
#include "ZRDDSCppSimpleInterface.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace DDS;
// 定义零拷贝数据类型回调接口
class BytesTypeListener :
virtual public SimpleDataReaderListener<Bytes, BytesSeq, BytesDataReader>
{
public:
virtual void on_process_sample(DataReader* reader, const Bytes& sample, const SampleInfo& info)
{
// 通过该语句获取收到的样本所属主题名称
const char* topicName = reader->get_topicdescription()->get_name();
// TODO 在此填写业务处理,接收端sample.userBuffer为用户负载数据,sample.userLength为用户负载长度,无需考虑预留空间
printf("received data(%s) from topic(%s)\n",
sample.value.get_contiguous_buffer(),
topicName);
}
};
int sub()
{
// 获取入口,并指明QoS配置
DomainParticipantFactory* factory = DDSIF::Init("./config/zrdds_security_qos.xml", "security_example");
if (factory == NULL) { printf("DDSIF::Initialize failed.\n"); return -1; }
// 加载内置插件
ReturnCode_t retCode = factory->load_security_plugin(
"BuiltinPlugin",
"ZRDDSSecBuiltinPlugin.dll",
"BuiltinSecPluginGetInstance",
"BuiltinSecPluginFinalize");
if (retCode != NULL)
{
printf("load_security_plugin failed(%d).", retCode);
return -1;
}
// 初始化授权的域参与者
DomainParticipant* authDP = DDSIF::CreateDP(150, "encry_example_sub");
if (NULL == authDP) { printf("DDSIF::CreateDP failed.\n"); return -2; }
// 创建encry_example_1数据读者,并设置监听器,期望成功
BytesTypeListener* m_listener = new BytesTypeListener();
DataReader* authDr = DDSIF::SubTopic(
authDP, "encry_example_1", BytesTypeSupport::get_instance(), "reliable", m_listener);
if (NULL == authDr) { printf("DDSIF::SubscribeTopic failed.\n"); return -5; }
// 创建encry_example_2数据读者,并设置监听器,由于权限配置文件中未指明能够订阅该主题,故而订阅失败
authDr = DDSIF::SubTopic(
authDP, "encry_example_2", BytesTypeSupport::get_instance(), "reliable", m_listener);
if (NULL == authDr) { printf("DDSIF::SubscribeTopic failed.\n"); return -5;}
getchar();
DDSIF::Finalize();
return 0;
}
int main(int argc, char* argv[])
{
return sub();
}
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]