天天看點

【物聯網智能網關-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 MicroFramework系統比較小的嵌入式裝置來說, 內建一個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區,下載下傳完畢後,用公鑰進行簽名驗證,驗證通過後(一是驗證來源是否可靠,另外就是驗證更新檔案的完整性),才置相關标志位,然後讓系統複位,複位後根據相關的标志,把相關資料轉移到正常程式區,并加載運作。

從以上步驟中可以看出,如果遠端更新過程沒有完成,原來的程式還将繼續保持,并不會被破壞。

更新流程示意圖如下:

【物聯網智能網關-18】多通道遠端安全更新

下面讓我們詳細介紹完整的遠端更新流程:

建立公鑰和私鑰

在示例“資料加密解密”中,我們提供了一個和.NET Micro Framework相對應的Windows版本的資料加密解密程式(界面如下圖所示)。其用到的加解密函數,都是.NET Framework的标準加密解密庫。

考慮到存儲空間,裝置端(.NET Micro Framework系統)的二進制格式的密鑰和上位機的二進制格式略有不同,實際要小一些,比如公鑰的指數,一般都固定為0x1,0x0,0x1三個位元組的數,是以就省略了。同理私鑰也是做了這樣的處理。

但是xml格式的都是一樣的,底層也提供了一個接口函數,可以直接導入XML格式的密鑰。

【物聯網智能網關-18】多通道遠端安全更新

為了各自的友善,我們為對應的PC端程式(遠端更新的發起方)提供的是XML格式的私鑰,為裝置端(被更新的一方)提供的是二進制格式的公鑰。

分别用剛才提到的工具進行導出。

導出私鑰的相關代碼其實很簡單,預設是1024位的Key,代碼如下:

RSACryptoServiceProvider mskey = new RSACryptoServiceProvider();

    stringxmlKey = mskey.ToXmlString(true);           

針對二進制格式的公鑰,其實我們僅導出128位元組的Modulus的資料:

byte[] key = new byte[128];

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

公鑰部署

采用最新版本的YFAccessFlash工具(V3.12.0以上),把導出的二進制格式的公鑰部署到裝置上(.NET Micro Framework系統)。

【物聯網智能網關-18】多通道遠端安全更新

公鑰提取及程式部署、校驗

以下代碼完成公鑰的提取(需要引用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, intoffset, int count);

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

    public delegate int GetReceiveCountHandle();            

第一個是發送資料用的,第二個是接收資料用的,第三個是擷取接收緩沖區資料個數的。并且這個YFSoft.WireProtocol.cs檔案,桌面.NET Framework和裝置.NET Micro Framework完全一樣的。

比如如果通信信道是序列槽,則這三個接口的實作代碼如下:

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

     {

          port.Write(buffer, offset, count);

          return0;

     }

 private intReceiveBytesHandle(byte[] buffer, int offset, intcount)

     {

         returnport.Read(buffer, offset, count);

     }

     private int GetReceiveCountHandle()

     {

         returnport.BytesToRead;

     }                 

如果是網口,則代碼如下:

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

     {

          returnsocket.Send(buffer, offset, count, SocketFlags.None);

     }

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

    {

         returnsocket.Receive(buffer, offset, count, SocketFlags.None);

     }

    private int GetReceiveCountHandle()

     {

         returnsocket.Available;

     }           

接下來,在裝置端主程式中,隻需要添加兩句代碼,就可以實作遠端更新的服務端功能。

如果是序列槽,代碼如下:

  //遠端更新服務(基于序列槽)

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

    tsu.Launch();           

如果是網口,代碼如下:

//遠端更新服務(基于TCP)

    TcpServerUpdatetsu = new TcpServerUpdate(10189,true);

    tsu.Launch()           

PC端的接口實作類似,但是遠端資料有所不同,由于序列槽方式和網口方式基本一樣,是以我們僅舉以太網通信方式。

具體的更新代碼如下:

   private void btnUpdate_Click(objectsender, EventArgs e)

    {

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

        {

            txtInfo.Text = ">>> 提取以下檔案的資料...\r\n";              

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

            MemoryStreamms = new MemoryStream();

            foreach(string file infiles)

            {

                txtInfo.Text += ">>> "+file + "\r\n";

                FileStreamtfs = 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 += ">>> 提取并合成資料成功!\r\n";

 

            //建立簽名檔案

            stringxmlPrivateKey = "<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>";

            RSACryptoServiceProviderrsa = new RSACryptoServiceProvider();

            rsa.FromXmlString(xmlPrivateKey);

 

            byte[]pe = ms.ToArray();

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

            txtInfo.Text += ">>> 對合成資料進行簽名成功!\r\n";

            txtInfo.Text += ">>> 正在更新...";

            Application.DoEvents();

            intret = +tcu.Update(pe, sis);

            if(ret == 0)

            {

                txtInfo.Text += "成功!\r\n";

            }

            else

            {

                txtInfo.Text += "失敗("+ ret.ToString() + ")!\r\n";

            }

        }           

    }           

更新示範

1、先運作以太網遠端更新示例下的MF程式。這個示例LED燈是慢閃的

2、運作遠端更新PC端程式(開發闆預設IP為192.168.1.100),連接配接成功後,選擇MFSample目錄下的

【物聯網智能網關-18】多通道遠端安全更新
【物聯網智能網關-18】多通道遠端安全更新

燈快速閃爍示例,然後遠端更新。如果成功,會看到燈快速閃爍。這個時候PC端程式斷開連接配接,重新連接配接,然後遠端部署燈慢速閃爍程式,交換部署,以便更好的觀察程式是否遠端部署成功。

源碼下載下傳

【物聯網智能網關-18】多通道遠端安全更新

源碼連結:

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

繼續閱讀