学习open62541---[12]加密(使⽤mbedTLS)
本⽂主要讲述OPC UA Client和OPC UA Server之间如何加密通信,这是个很重要的功能。在之前的系列⽂章⾥,都没有使⽤加密,⽐较明显的是Server启动时的打印,如下,
提⽰没有安全策略,可能会造成泄密。
另外,在使⽤UaExpert连接Server时,Server左侧图标是个红⾊的锁,⽽且这个锁是打开的,表⽰没有安全策略。
以上这些都表⽰未加密。
本⽂通过mbedTLS实现加密通信,环境如下,
OS:Debian10,Ubuntu也是⼀样
open62541版本:v1.1.6
mbedTLS版本:2.26.0
open62541在v1.1.1版本之后开始⽀持OpenSSL加密,可以参考,OpenSSL在桌⾯端应⽤⼴泛,mbedTLS主要⽤在嵌⼊式领域,桌⾯端也可以使⽤。
⼀⽣成⾃签名证书和私匙
想实现加密通信,就需要⾃签名证书和私匙,所以⾸先要⽣成它们。如果了解HTTPS的话就⽐较好理解,如果不了解也没问题,按照操作做就可以了。
使⽤open62541⾃带⼯具⽣成
在open62541源码根⽬录下的tools/certs⽬录⾥,有2个⽂件,
可以使⽤这个python脚本去⽣成证书和私匙,但需要预先安装⼀个python模块 — netifaces,输⼊以下命令安装,
pip3 install netifaces
PS:使⽤pip3是因为我们会使⽤python3去运⾏这个脚本。
安装完netifaces后,我们先看下这个脚本的帮助信息,输⼊以下命令,
python3 create_self-signed.py  -h
帮助信息如下,
其中-u是最重要的参数,⽤于指定证书的URI值,这个值后⾯会讲到,代码⾥需要设置成相同值。
验证加密通信需要server和client,它们都需要证书和私匙。⾸先⽣成server的证书,执⾏下⾯的命令去⽣成,python3 create_self-signed.py ./ -u urn:open62541.server.application -c server
接着⽣成client的证书,
python3 create_self-signed.py ./ -u urn:open62541.client.application -c client
这样就会在当前⽬录下⽣成server_cert.der,server_key.der,client_cert.der,client_key.der这4个⽂件
可以使⽤如下命令来查看server证书的URI参数,cmake如何使用
openssl x509 -in server_cert.der -inform der -noout -text
如下,
URI值和前⾯-u参数后的值是⼀样的。同理,可以查看client证书⾥URI值。
最后创建⼯程⽬录,然后把证书拷贝出来,
encryption_mbedtls即使我们的⼯程⽬录,读者可以根据需求随意创建。
⼆编译mbedTLS
下载下来后,cd到其源码⽬录,然后按如下步骤操作,
新建build⽬录并cd进⼊
执⾏cmake .. && make
把源码⽬录下的include⽬录和build⽬录下的library⽬录拷贝出来(build⽬录下也有include⽬录,⾥⾯是符号链接,所以不需要)清理library⽬录,只保留三个库⽂件,最后结构如下,
三编译open62541
1. 配置
在open62541源码⽬录下的⾥到以下4个option,
UA_ENABLE_AMALGAMATION
UA_ENABLE_ENCRYPTION
UA_ENABLE_ENCRYPTION_OPENSSL
UA_ENABLE_ENCRYPTION_MBEDTLS
把第1,2和4改为ON,第3改为OFF
2. 查mbedTLS
打开open62541源码⽬录下的tools/ake,有如下内容,
#check environment variable
if("$ENV{MBEDTLS_FOLDER_INCLUDE}")
set(MBEDTLS_FOLDER_INCLUDE "$ENV{MBEDTLS_FOLDER_INCLUDE}")
endif()
if("$ENV{MBEDTLS_FOLDER_LIBRARY}")
set(MBEDTLS_FOLDER_LIBRARY "$ENV{MBEDTLS_FOLDER_LIBRARY}")
endif()
CMake变量MBEDTLS_FOLDER_INCLUDE和MBEDTLS_FOLDER_LIBRARY分别⽤于存放mbedTLS的头⽂件和库⽂件。这些语句的意思是从环境变量⾥查mbedTLS,由于我们是单独编译的,并没有在环境变量⾥设置其位置,所以需要进⾏修改。
把上节中mbedTLS的存放⽬录添加进来就⾏了,如下,
#check environment variable
if("$ENV{MBEDTLS_FOLDER_INCLUDE}")
set(MBEDTLS_FOLDER_INCLUDE "$ENV{MBEDTLS_FOLDER_INCLUDE}")
else()
set(MBEDTLS_FOLDER_INCLUDE "/home/wh/work/opcua/encryption_mbedtls/mbedtls/include")
endif()
if("$ENV{MBEDTLS_FOLDER_LIBRARY}")
set(MBEDTLS_FOLDER_LIBRARY "$ENV{MBEDTLS_FOLDER_LIBRARY}")
else()
set(MBEDTLS_FOLDER_LIBRARY "/home/wh/work/opcua/encryption_mbedtls/mbedtls/library")
endif()
这⾥使⽤的是绝对路径,也可以使⽤相对路径,简单测试⼀下就⾏了。
当然,也可以不修改ake,在执⾏cmake命令时指定⼀下这2个变量的值就⾏了,下⼀步会讲。
3. 编译
按如下步骤操作,
1. cd到open62541源码⽬录下,新建build⽬录并cd进⼊
2. 执⾏cmake .. && make
3. 把open62541.h和bin下的libopen62541.a拷贝到⼯程⽬录下的open62541⽬录⾥,
如果第2步没有修改ake,那么执⾏cmake命令时就要如下这样,
cmake -DMBEDTLS_FOLDER_INCLUDE=/home/wh/work/opcua/encryption_mbedtls/mbedtls/include -DMBEDTLS_FOLDER_LIBRARY=/home/wh/work/op cua/encryption_mbedtls/mbedtls/library  ..&&make
四验证加密通信
这⾥使⽤代码和UaExpert来验证加密通信。
1. 使⽤代码验证
这⾥的测试代码使⽤open62541⾃带的example代码,但是有点⼩坑需要填⼀下。
client.c代码如下,配置URI的地⽅是新加的(第47~48⾏),要和证书⾥的URI值⼀样
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
* See /publicdomain/zero/1.0/ for more information. */
#include<stdlib.h>
#include"common.h"
#define MIN_ARGS 4
int main(int argc,char* argv[]){
if(argc < MIN_ARGS){
UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Arguments are missing. The required arguments are "
"&p://host:port> "
"&p://host:port> "
"<client-certificate.der> <client-private-key.der> "
"[<trustlist1.der>, ...]");
return EXIT_FAILURE;
}
const char*endpointUrl = argv[1];
/* Load certificate and private key */
UA_ByteString certificate =loadFile(argv[2]);
UA_ByteString privateKey  =loadFile(argv[3]);
/* Load the trustList. Load revocationList is not supported now */
size_t trustListSize =0;
if(argc > MIN_ARGS)
trustListSize =(size_t)argc-MIN_ARGS;
UA_STACKARRAY(UA_ByteString, trustList, trustListSize);
for(size_t trustListCount =0; trustListCount < trustListSize; trustListCount++)
trustList[trustListCount]=loadFile(argv[trustListCount+4]);
UA_ByteString *revocationList =NULL;
size_t revocationListSize =0;
UA_Client *client =UA_Client_new();
UA_ClientConfig *cc =UA_Client_getConfig(client);
cc->securityMode = UA_MESSAGESECURITYMODE_SIGNANDENCRYPT;
cc->securityPolicyUri =UA_STRING_ALLOC("/UA/SecurityPolicy#Basic128Rsa15");
UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey,
trustList, trustListSize,
revocationList, revocationListSize);
// 填坑的地⽅,⾮常重要
UA_String_deleteMembers(&cc->clientDescription.applicationUri);
cc->clientDescription.applicationUri =UA_STRING_ALLOC("urn:open62541.client.application");
UA_ByteString_clear(&certificate);
UA_ByteString_clear(&privateKey);
for(size_t deleteCount =0; deleteCount < trustListSize; deleteCount++){
UA_ByteString_clear(&trustList[deleteCount]);
}
/* Secure client connect */
cc->securityMode = UA_MESSAGESECURITYMODE_SIGNANDENCRYPT;/* require encryption */
UA_StatusCode retval =UA_Client_connect(client, endpointUrl);
if(retval != UA_STATUSCODE_GOOD){
UA_Client_delete(client);
return EXIT_FAILURE;
}
UA_Variant value;
UA_Variant_init(&value);
/* NodeId of the variable holding the current time */
const UA_NodeId nodeId =UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);    retval =UA_Client_readValueAttribute(client, nodeId,&value);
if(retval == UA_STATUSCODE_GOOD &&
UA_Variant_hasScalarType(&value,&UA_TYPES[UA_TYPES_DATETIME])){
UA_DateTime raw_date  =*(UA_DateTime *) value.data;
UA_DateTimeStruct dts =UA_DateTime_toStruct(raw_date);
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"date is: %u-%u-%u %u:%u:%u.%03u\n",                    dts.day, h, ar, dts.hour, dts.min, dts.sec, dts.milliSec);
}
/* Clean up */
UA_Variant_clear(&value);

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。