【物聯網智能網關-18】多通道遠程安全升級

一個典型的物聯網系統,每每有上百個,甚至成千上萬個聯網節點,而且每一個節點聯網的方式可能不一樣,好比有Zigbee聯網,有430/470M無線方式聯網,有GPRS/3G無線模塊聯網,有RS485/CAN總線方式聯網,有以太網TCP/IP方式聯網等等。如此種種節點,若是須要進行程序更新,這是一個很是使人頭疼的事。安全

另外,隨着物聯網各類項目的大量實施,在運行維護過程當中,其技術人員的交通住宿成本及人力成本的逐年增長,讓設備的遠程維護,遠程升級功能變得愈來愈重要了。服務器

可是要實現遠程升級,也不是一件容易的事,須要重點解決以下三方面的事情:第一就是安全,如何防止有惡意的人篡改和遠程升級相關設備的程序;第二就是遠程升級通訊信道的問題,好比國內的GPRS和3G模塊,因爲被分配的IP地址不是公網地址,因此必須反連,也就是說模塊要主動請求服務器進行遠程升級,而不是常規程序升級的作法,直接訪問設備對設備進行升級,另外就是各類通訊接口的支持,好比Zigbee,430/470M無線信道,因爲這類設備,每次傳輸的字節數有限,實現遠程升級功能也要充分考慮到這一點。第三就是可靠升級,好比升級一半,設備忽然掉電,如何確保設備不變磚。socket

基於.NET Micro Framework系統的物聯網智能網關,分別採用以下技術來解決以上所提到的問題。ide

【安全問題】函數

.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相關代碼,算相對完美的解決了這個問題。(關於加解密的詳情,後續有專門的文件進行介紹)。.net

遠程升級的思路:3d

首先對要升級的文件進行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 = ">>> 提取如下文件的數據...\r\n";              

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

            MemoryStream ms = new MemoryStream();

            foreach (string file in files)

            {

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

                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 += ">>> 提取併合成數據成功!\r\n";

 

            //建立簽名文件

            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 += ">>> 對合成數據進行簽名成功!\r\n";

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

            Application.DoEvents();

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

            if (ret == 0)

            {

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

            }

            else

            {

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

            }

        }           

    }

升級演示

一、先運行以太網遠程升級示例下的MF程序。這個示例LED燈是慢閃的

二、運行遠程升級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

相關文章
相關標籤/搜索