其他分享
首页 > 其他分享> > Win10+VS2017实现OPC UA加密服务器(使用Openssl库)

Win10+VS2017实现OPC UA加密服务器(使用Openssl库)

作者:互联网

本文利用VS2017和Openssl库实现OPC UA安全服务器,主要包括

1. Windows中Openssl库的安装和使用
2. 服务器证书的生成,信任列表的更新
3. VS2017中Openssl开发环境的配置
4. 支持加密的open62541源文件的编译生成及错误处理

文本参考了许多大佬的博客,都附在文章末尾

1. Windows安装Openssl库

这步很简单,http://slproweb.com/products/Win32OpenSSL.html寻找对应版本下载安装, 我的电脑是64位的,故选择64位非轻量级的msi格式文件进行安装,安装成功后将openssl的bin目录添加到系统的环境变量path中,如下图所示

重启。(超大声)

测试是否安装成功,打开命令行窗口,输入命令"openssl"

出现上图结果,第一步操作成功。

2. OPC UA服务器安全证书和私钥文件生成,以及和客户端之间的信任列表更新

使用open62541自带的工具生成证书和私钥文件,从open62541的官方github仓库中下载源码包,查看open62541源码目录tools/certs,运行这个python脚本即可生成证书和私钥。

从命令行进入这个目录,先安装netifaces库

接着运行python脚本

运行命令在没有参数的情况下默认生成服务器的证书和私钥文件。

运行成功后会在原文件夹下生成证书和私钥文件,如下图所示。

打开uaexpert,如下图所示,依次点击Settings——>Manage Certificates

在弹出的对话框中点击Open Certificate Location

上图中的uaexpert.der就是客户端的证书,将这个证书复制到服务器证书所在的文件夹中。

进入uaexpert/PKI/trusted/certs目录下,将刚刚生成的服务器证书复制到该文件夹中,如下图所示。

至此,就完成了服务器证书和私钥的生成,以及服务器和客户端的相互信任。

3. VS2017中Openssl开发环境的配置

首先在VS2017中新建工程,这一步比较简单不赘述。

因为刚刚的Openssl库是64位的,所以在VS2017中最好选择解决方案平台x64,否则会出现不兼容的警告,在解决方案中选择项目——>属性,点击VC++目录,如下图。

在包含目录中选择OpenSSL安装目录中的include文件夹,在库目录中选择OpenSSL安装目录中的lib文件夹。

点击链接器——>输入,如下图所示

在附加依赖项中添加libssl.lib,libcrypto.lib以及WS2_32.Lib。

最后一步将OpenSSL安装目录下的两个dll文件,如下图所示复制到工程目录下,如下图所示

至此,VS的Openssl开发环境基本配置完成。

4. 支持加密的open62541源文件的编译生成及错误处理

按照,open62541官方文档中的build on windows部分进行操作,我在这里使用的是可视化的cmake应用,在选择编译options时,必须勾选

  1. UA_ENABLE_AMALGAMATION
  2. UA_ENABLE_ENCRYPTION
  3. UA_ENABLE_ENCRYPTION_OPENSSL

然后按照文档中的说明进行build,最后生成支持加密等安全操作的open62541.c和open62541.h文件。

在刚刚新建的工程中加入如下图所示的源文件和头文件。

其中open62541.c和open62541.h是刚刚build完成的。common.h如下:

/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
 * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */

#include "open62541.h"

/* loadFile parses the certificate file.
 *
 * @param  path               specifies the file name given in argv[]
 * @return Returns the file content after parsing */
static UA_INLINE UA_ByteString
loadFile(const char *const path) {
    UA_ByteString fileContents = UA_STRING_NULL;

    /* Open the file */
    FILE *fp = fopen(path, "rb");
    if(!fp) {
        errno = 0; /* We read errno also from the tcp layer... */
        return fileContents;
    }

    /* Get the file length, allocate the data and read */
    fseek(fp, 0, SEEK_END);
    fileContents.length = (size_t)ftell(fp);
    fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
    if(fileContents.data) {
        fseek(fp, 0, SEEK_SET);
        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
        if(read != fileContents.length)
            UA_ByteString_clear(&fileContents);
    } else {
        fileContents.length = 0;
    }
    fclose(fp);

    return fileContents;
}

ServerEnc.c如下(我将证书文件都放在了D:/certs下):

/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
 * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
 *
 *    Copyright 2019 (c) Kalycito Infotech Private Limited
 *
 */

#include <signal.h>
#include <stdlib.h>
#include <openssl/x509v3.h>
#include "common.h"
#pragma comment(lib, "ws2_32.lib") 

UA_Boolean running = true;
static void
stopHandler(int sig) {
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
    running = false;
}

int main(int argc, char *argv[]) {
    signal(SIGINT, stopHandler);
    signal(SIGTERM, stopHandler);
/*
    if(argc < 3) {
        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                     "Missing arguments. Arguments are "
                     "<server-certificate.der> <private-key.der> "
                     "[<trustlist1.der>, ...]");
        return EXIT_FAILURE;
    }*/

    /* 加载server的证书和私匙 */
    /*UA_ByteString certificate = loadFile(argv[1]);
    UA_ByteString privateKey = loadFile(argv[2]);*/
    UA_ByteString certificate = loadFile("D://certs//server_cert.der");
    UA_ByteString privateKey = loadFile("D://certs//server_key.der");

    /* 加载trustlist */
    size_t trustListSize = 1;
    /*if(argc > 3)
        trustListSize = (size_t)argc - 3;*/
    UA_STACKARRAY(UA_ByteString, trustList, trustListSize);
    //for(size_t i = 0; i < trustListSize; i++)
    //    //trustList[i] = loadFile(argv[i + 3]);
    //    trustList[i] = loadFile("D://certs//uaexpert.der");
    trustList[0] = loadFile("D://certs//uaexpert.der");
  
    /* Loading of a issuer list, not used in this application */
    size_t issuerListSize = 0;
    UA_ByteString *issuerList = NULL;

    /* Loading of a revocation list currently unsupported */
    UA_ByteString *revocationList = NULL;
    size_t revocationListSize = 0;

    UA_Server *server = UA_Server_new();
    UA_ServerConfig *config = UA_Server_getConfig(server);

    UA_StatusCode retval = UA_ServerConfig_setDefaultWithSecurityPolicies(
        config, 4840, &certificate, &privateKey, trustList, trustListSize, issuerList,
        issuerListSize, revocationList, revocationListSize);

    // 填坑的地方,非常重要,URI需要保证和证书里的URI一致
    config->applicationDescription.applicationUri =
        UA_STRING_ALLOC("urn:open62541.server.application");

    UA_ByteString_clear(&certificate);
    UA_ByteString_clear(&privateKey);
    for(size_t i = 0; i < trustListSize; i++)
        UA_ByteString_clear(&trustList[i]);
    if(retval != UA_STATUSCODE_GOOD)
        goto cleanup;

    retval = UA_Server_run(server, &running);

cleanup:
    UA_Server_delete(server);
    return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}

启动项目,即可运行。

实现过程中遇到的错误及解决

至此,基于Windows的加密服务器可以成功运行了。以上是大致的实现过程,还存在很多问题,望批评指正。

参考博客:

[1]https://blog.csdn.net/whahu1989/article/details/107281639

[2]https://www.jb51.net/article/119025.htm

 

 

 

 

 

 

 

标签:open62541,证书,fileContents,VS2017,Openssl,loadFile,OPC,ByteString,UA
来源: https://blog.csdn.net/gm_oct/article/details/110039322