zoukankan      html  css  js  c++  java
  • 【物联网智能网关-18】多通道远程安全升级

    一个典型的物联网系统,往往有上百个,甚至成千上万个联网节点,并且每个节点联网的方式可能不同,比如有Zigbee联网,有430/470M无线方式联网,有GPRS/3G无线模块联网,有RS485/CAN总线方式联网,有以太网TCP/IP方式联网等等。如此种种节点,如果需要进行程序更新,这是一个非常令人头疼的事。

    另外,随着物联网各种项目的大量实施,在运行维护过程中,其技术人员的交通住宿成本及人力成本的逐年增加,让设备的远程维护,远程升级功能变得越来越重要了。

    但是要实现远程升级,也不是一件容易的事,需要重点解决如下三方面的事情:第一就是安全,如何防止有恶意的人篡改和远程升级相关设备的程序;第二就是远程升级通信信道的问题,比如国内的GPRS和3G模块,由于被分配的IP地址不是公网地址,所以必须反连,也就是说模块要主动请求服务器进行远程升级,而不是常规程序升级的做法,直接访问设备对设备进行升级,另外就是各种通信接口的支持,比如Zigbee,430/470M无线信道,由于这类设备,每次传输的字节数有限,实现远程升级功能也要充分考虑到这一点。第三就是可靠升级,比如升级一半,设备突然掉电,如何确保设备不变砖。

    基于.NET Micro Framework系统的物联网智能网关,分别采用如下技术来解决以上所提到的问题。

    【安全问题】

    .NET Micro Framework系统,在V4.1版本之前,一直提供了两种加解密方法,一种是非对称加密RSA和对称加密XTEA(这部分代码一直没有开源,提供各种ARM版本和X86的连接库),但是这个库其中的RSA有两个致命的问题,一个是其生成的公钥和私钥无法和Windows或其他平台的目前已有的方法提供的一致,互不通用。另外一个问题就是其X86版本和嵌入式版本,其加解密竟然无法实现互解(另外也发现嵌入式版本的RSA加密,无法对特定长度的明文进行加解密)。

    4.2版本之后,封装了OpenSSL中的RSA、AES,DES等加密方法,但是对一些运行.NET Micro Framework系统比较小的嵌入式设备来说, 集成一个OpenSSL库有些太大了,大概增加400K字节大小,比一个.NET Micro Framework系统还要大,这是让人无法承受的。

    后续重新自行调整和构建了RSA相关代码,算相对完美的解决了这个问题。(关于加解密的详情,后续有专门的文件进行介绍)。

    远程升级的思路:

    首先对要升级的文件进行SHA1哈希计算,拼装成标准签名文件后,用RSA的私钥对该数据进行加密 –- 对升级文件进行签名。

    考虑到嵌入式系统RAM空间有限,数据可以分批,分片写入到Flash区域中,等完全写入完毕后,直接在Flash上(非NandFlash,可以直接访问),用预先下载好的公钥进行签名验证。

    【信道问题】

    其实.NET Micro Framework系统也提供了官方的远程升级方案,甚至为了实现这个功能,新提供一个MicroBooter来配合完成远程升级。但是其通信信道是基于.NET Micro Framework调试口的,这就有一个很大的问题,比如GPRS/3G通信设备无法远程直接访问,Zigbee,430/470M无线模块,每次传输的数据有限,都无法被配置为调试通道。

    我们的解决方案是,信道和具体的升级功能功能完全剥离,这样信道的问题,就简化成如何远程获取升级文件和签名的问题。针对这个问题,可实现的方案有很多种,也可以分为主动式和被动式升级。

    【可靠升级】

    我们采取的方案是,接收到的数据,先更新到一个系统Flash区,下载完毕后,用公钥进行签名验证,验证通过后(一是验证来源是否可靠,另外就是验证升级文件的完整性),才置相关标志位,然后让系统复位,复位后根据相关的标志,把相关数据转移到正常程序区,并加载运行。

    从以上步骤中可以看出,如果远程升级过程没有完成,原来的程序还将继续保持,并不会被破坏。

    升级流程示意图如下:

     

    下面让我们详细介绍完整的远程升级流程:

    创建公钥和私钥

    在示例“数据加密解密”中,我们提供了一个和.NET Micro Framework相对应的Windows版本的数据加密解密程序(界面如下图所示)。其用到的加解密函数,都是.NET Framework的标准加密解密库。

    考虑到存储空间,设备端(.NET Micro Framework系统)的二进制格式的密钥和上位机的二进制格式略有不同,实际要小一些,比如公钥的指数,一般都固定为0x1,0x0,0x1三个字节的数,所以就省略了。同理私钥也是做了这样的处理。

    但是xml格式的都是一样的,底层也提供了一个接口函数,可以直接导入XML格式的密钥。

     

    为了各自的方便,我们为对应的PC端程序(远程升级的发起方)提供的是XML格式的私钥,为设备端(被升级的一方)提供的是二进制格式的公钥。

    分别用刚才提到的工具进行导出。

    导出私钥的相关代码其实很简单,默认是1024位的Key,代码如下:

      RSACryptoServiceProvider mskey = new RSACryptoServiceProvider();

        string xmlKey = mskey.ToXmlString(true);

    针对二进制格式的公钥,其实我们仅导出128字节的Modulus的数据:

      byte[] key = new byte[128];

        Array.Copy(Modulus, 0, key, 0, 128);

    公钥部署

    采用最新版本的YFAccessFlash工具(V3.12.0以上),把导出的二进制格式的公钥部署到设备上(.NET Micro Framework系统)。

     

     公钥提取及程序部署、校验

    以下代码完成公钥的提取(需要引用YFSoft.Config库)

    byte[] PublicKey = new byte[128];

        if (YFSoft.Config.Read("PublicKey", PublicKey, 0, 128) == 0)

    {

        //…

    }

    分以下四步分别完成程序的部署和校验(需要引用YFSoft.RemoteUpgrade库)

    第一步:清空系统部署区

    RemoteUpgrade.Initialize();  

    第二步:向系统数据区写数据(可以分块写)

    RemoteUpgrade.Write(0, peFile, 0, peFile.Length);

    第三步:RSA+SHA1校验

    bool IsVerifyOK = (RemoteUpgrade.Verify(peFile.Length, Signature, PublicKey)==0);

    第四步:置标志位,系统重启

        RemoteUpgrade.Finish();

       

    升级文件及签名数据远程传输

    以上几步,基本上流程一致,没有多少变化,但是这一步,变数比较大。从物理信道上来说,可以是以太网、Wifi、蓝牙、无线(430M/470M)、Zigbee、RS232/RS485串口通信、GPRS/3G等等。

    即使同一个信道,采用的升级策略也不相同,比如可以是主动式,和被动式。所谓的主动式,就是远程可以直接对远程的模块进行升级,是相对即时的一种的方式。而被动式,是远程模块,每隔特定的时间间隔,主动去访问指定的服务器,根据获取的信息,判断是否该升级。比如国内通过GPRS/3G通道,也只能采用这种方式了。

    我们提供的两个例子,一个是基于以太网TCP/IP通信,一个是基于串口通信,都是主动式的。不过例子中提供了一个YFSoft.WireProtocol源码类,和特定的信道,还有主动被动都没有关系,只需要实现相关的委托接口就可以完成远程升级文件的接收或发送。

    要实现的三个接口分别是:

    public delegate int TransmitBytesHandle(byte[] buffer, int offset, int count);

        public delegate int ReceiveBytesHandle(byte[] buffer, int offset, int count);

        public delegate int GetReceiveCountHandle();

    第一个是发送数据用的,第二个是接收数据用的,第三个是获取接收缓冲区数据个数的。并且这个YFSoft.WireProtocol.cs文件,桌面.NET Framework和设备.NET Micro Framework完全一样的。

    比如如果通信信道是串口,则这三个接口的实现代码如下:

    private int TransmitBytesHandle(byte[] buffer, int offset, int count)

         {

              port.Write(buffer, offset, count);

              return 0;

         }

     private int ReceiveBytesHandle(byte[] buffer, int offset, int count)

         {

             return port.Read(buffer, offset, count);

         }

         private int GetReceiveCountHandle()

         {

             return port.BytesToRead;

         }      

    如果是网口,则代码如下:

    private int TransmitBytesHandle(byte[] buffer, int offset, int count)

         {

              return socket.Send(buffer, offset, count, SocketFlags.None);

         }

         private int ReceiveBytesHandle(byte[] buffer, int offset, int count)

        {

             return socket.Receive(buffer, offset, count, SocketFlags.None);

         }

        private int GetReceiveCountHandle()

         {

             return socket.Available;

         }

       

    接下来,在设备端主程序中,只需要添加两句代码,就可以实现远程升级的服务端功能。

    如果是串口,代码如下:

        //远程升级服务(基于串口)

        PortServerUpdate tsu = new PortServerUpdate("COM1",115200,true);

        tsu.Launch();

    如果是网口,代码如下:

    //远程升级服务(基于TCP)

        TcpServerUpdate tsu = new TcpServerUpdate(10189,true);

        tsu.Launch()

    PC端的接口实现类似,但是远程数据有所不同,由于串口方式和网口方式基本一样,所以我们仅举以太网通信方式。

    具体的升级代码如下:

        private void btnUpdate_Click(object sender, EventArgs e)

        {

            if (txtFiles.Text != null && txtFiles.Text.Length > 0)

            {

                txtInfo.Text = ">>> 提取以下文件的数据... ";              

                var files = Directory.EnumerateFiles(txtFiles.Text, "*.pe");

                MemoryStream ms = new MemoryStream();

                foreach (string file in files)

                {

                    txtInfo.Text += ">>> "+file + " ";

                    FileStream tfs = new FileStream(file, FileMode.Open, FileAccess.Read);

                    byte[] buffer = new byte[tfs.Length];

                    tfs.Read(buffer, 0, buffer.Length);

                    tfs.Close();

                    ms.Write(buffer, 0, buffer.Length);

                }

                txtInfo.Text += ">>> 提取并合成数据成功! ";

     

                //创建签名文件

                string xmlPrivateKey = "<RSAKeyValue><Modulus>vBjTkuBruYPSBE5y5T7hd4KEADy6UuBk1v+Es8BOvggOsfEGvxJNraDTCxTPaNhVkbaCFIavw8amXoIkFzjDw7fV3JVDflsqZ4qg23DOcWz/DvF+12sNcXTsHX7HELJYObZI1lo1kE2fFej1uuRzr7v9DgoFurgg9tGU9gD3dCU=</Modulus><Exponent>AQAB</Exponent><P>4NIKd4E+/0RRT58FZriPTWkfysy8c1Hl7CbeKet1m7KDsatwxNYm6u+oKltmG8UQ73pUfsEbBFpCo/UgL36osQ==</P><Q>1i73MFObZqHBvAhBP+uAbDa33k9yBqePDC0lKL5bqw4AhWzKb3EJd0OWUYf+LxZKnYbmbYNIMolDskcnbPjftQ==</Q><DP>2L7HJoWthY6I0blfDLRcO+ZgpzURbiCECVNDlqiRvySwwIandq174b5ho0xwuc8Yz7hhY76qXFzkqIt3lzKGUQ==</DP><DQ>lkj0F0vC8bu0dZyRNCmpvcSTNYEnMDYoMFIJDdKr/ZVglj5kuNdm3fFlqyWyHBYXGvtJ+jOw2AzqnFBDALqMNQ==</DQ><InverseQ>jQhrgi6tUQ0XpH1QIzLmHEMZn1PukayA+5tBps3SCswnFQC3iSW58N/m2YX2Z37USXQtqG8/HmZTyrUdAzkgMA==</InverseQ><D>hzCUyCjyY/ihdqTnoWqrZFjzBLSg+jX7ZCdsOkFKlvx1i2D/h07hc5x2cq13URTDk6IIJjaTl3NsWdrRk7shv4sXcW5bKg57GPvV6CHRij0Af1xQRLpYsgzeyVjRgKaU+Ea9KZV+mYQ8Ey56krF8MW0/Y4IsVvc1sGWG3HixFEE=</D></RSAKeyValue>";

                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

                rsa.FromXmlString(xmlPrivateKey);

     

                byte[] pe = ms.ToArray();

                byte[] sis = rsa.SignData(pe, typeof(System.Security.Cryptography.SHA1));

                txtInfo.Text += ">>> 对合成数据进行签名成功! ";

                txtInfo.Text += ">>> 正在升级...";

                Application.DoEvents();

                int ret = +tcu.Update(pe, sis);

                if (ret == 0)

                {

                    txtInfo.Text += "成功! ";

                }

                else

                {

                    txtInfo.Text += "失败("+ ret.ToString() + ")! ";

                }

            }           

        }

    升级演示

    1、先运行以太网远程升级示例下的MF程序。这个示例LED灯是慢闪的

    2、运行远程升级PC端程序(开发板默认IP为192.168.1.100),连接成功后,选择MFSample目录下的

     

    灯快速闪烁示例,然后远程升级。如果成功,会看到灯快速闪烁。这个时候PC端程序断开连接,重新连接,然后远程部署灯慢速闪烁程序,交换部署,以便更好的观察程序是否远程部署成功。

     

    源码下载

    源码链接:http://www.yfiot.com/MFRelease/Sample/RemoteUpgradeSample.rar

    --------------------------------------------------------------------------------------

    MF简介:http://blog.csdn.net/yefanqiu/article/details/5711770

    MF资料:http://www.yfiot.com/DownloadList.asp?Id=2&page=1

  • 相关阅读:
    Java JMX 监管
    Spring Boot REST(一)核心接口
    JSR 规范目录
    【平衡树】宠物收养所 HNOI 2004
    【树型DP】叶子的颜色 OUROJ 1698
    【匈牙利匹配】无题II HDU2236
    【贪心】Communication System POJ 1018
    【贪心】Moving Tables POJ 1083
    Calling Extraterrestrial Intelligence Again POJ 1411
    【贪心】Allowance POJ 3040
  • 原文地址:https://www.cnblogs.com/yefanqiu/p/3624323.html
Copyright © 2011-2022 走看看