如何設計簽署的XML檔案 (XML Signature)

如何設計簽署的XML檔案 (XML Signature)

最近因為專案上的需求,需要使用服務 (Web Service/Web API) 來交換簽署過的 XML 資料,因為為了保證資料沒有被竄改,因此可透過 XML Signature 來簽署 XML 資料,如過使用非對稱來加密 XML 資料,那麼對方就一定得用私密的金鑰組來驗證此 XML 沒有遭到竄改。

首先,我們先來了解什麼是 XML Signature?

 

XML Signature

也稱作XMLDsig,XML-DSig,XML-Sig,是一個定義數字簽名的XML語法的W3C建議的標準。透過XML signature可以用來簽名任何類型的資源,最常見的是XML文件檔,但是任何可以通過URL訪問的資源都可以被簽名。

 

XML Signature 結構

含一個 Signature 元素,是一個以 Signature 為根的一個 XML 結構,其中包含了最重要的標籤 SignedInfo,定義了如何「圈選」要簽署的資料區塊,如何進行資料轉換與計算摘要等,以其最後如何簽章的訊息。而 SignatureValue 則放置實際上簽章的簽章值。KeyInfo是描述與簽署此份文件的金鑰訊息。而 Object 的標籤則是一個活用的欄位,可以描述資料的來源或描述簽章其他相關訊息 SignatureProperty,如Time Stamp 等等... 其名字空間為 http://www.w3.org/2000/09/xmldsig#

基本結構如下所示:

image

 

XML 簽章的類型
XML簽章可以分成三種類型:

  1. Enveloping Signatures
  2. Enveloped Signatures
  3. Detached Signatures

其中主要差異為,Enveloping Signatures 與 Enveloped Signatures 是將 XML 資料與簽章放置在同一個 XML 文件檔案。而 Detached Signatures 顧名思義就是簽章的內容與欲簽署的 XML 資料是放置在不同的 XML 文件檔案內。

 

何謂 XML 正規化?

為何需要做 XML 正規化呢?這是由於XML簽章簽章的對象主要是 XML 資料內容的本身,並不是使用資料的二進位碼。例如:我們簽署一個如:

<測試根結點>
<測試 >
  <子測試>Toyota Corolla Altis 2014</子測試>
  </測試 >
</測試根結點>

類似這樣一個簡單的XML,如果使用傳統的簽章方式 (PKCS#7) 則會將這一段文字包含空白轉換為二進位碼,在對此二進位碼進行簽章。因此,在 XML 的標簽中若有空白,如下:

image

那麼會被識別是不同的 XML,誤以為被串改,所以才需要做 XML 正規化。

另外如果編碼不同,比如來源是 UTF-8,但目的以 big5 去解簽章這樣也是不行的。

註:

如需 XML 加密標準的詳細資訊,請參閱全球資訊網協會 (W3C) 的 XML 加密規格,網址為 http://www.w3.org/TR/xmldsig-core/

 

以 C# 來實做 XML Signature

如何以 C# 來實做 XML Signature 呢?在.NET Framework 中的 System.Security.Cryptography.Xml 命名空間中即支援各種簽署 XML 相關的類別,以數位簽章簽署 XML 文件或部分 XML 文件。

再開始之前,首先,先建立一個簡單的 Web Service ,裡面的 Web Method 只傳回一個簡單的類別:

   1:  [WebMethod]
   2:  public Balloon GetChangedXML()
   3:  {
   4:      return new Balloon()
   5:      {
   6:          MyBalloonChilder = new BalloonChilder()
   7:          {
   8:              CarName = "Toyota Corolla Altis 2014"
   9:          }
  10:      };
  11:  }

 

回傳的 Balloon 類別定義如下:

   1:  public class BalloonChilder
   2:  {
   3:      [XmlElement(ElementName = "子測試")]
   4:      public string CarName
   5:      {
   6:          get { return this._carName; }
   7:          set { this._carName = value; }
   8:      }
   9:      private string _carName { get; set; }
  10:  }
  11:  [XmlRoot(ElementName="測試根結點")]
  12:  public class Balloon
  13:  {
  14:      private BalloonChilder _myBalloonChilder;
  15:      [XmlElement(ElementName="測試")]
  16:      public BalloonChilder MyBalloonChilder
  17:      {
  18:          get { return this._myBalloonChilder; }
  19:          set { this._myBalloonChilder = value; }
  20:      }
  21:  }

這個 Web Service 回傳結果如下:

image

如上的 XML 文件還未加上 XML Signature 數位簽章,透過加簽的方式 XML Signature (一般稱為 XMLDSIG) 可讓您確保資料在簽署之後並未遭到修改。

下面示範如何建立 RSA 簽署金鑰,將此金鑰加入至安全金鑰容器,然後利用金鑰來數位簽署 XML 文件數位簽署整份 XML 文件,並將簽章附加到 <Signature> 項目的文件中。

 

一、建立密碼編譯計算之密碼編譯服務供應者Cryptography Service Provider (CSP)

   1:  CspParameters cspParams = new CspParameters();
   2:  cspParams.KeyContainerName = "GELIS_RSA_KEY";

 

二、使用 RSACryptoServiceProvider 類別產生非對稱金鑰

   1:  RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);

 

三、將原本的 Web Method 的程式碼改為如下就可對XML文件進行簽署

   1:  [WebMethod]
   2:  public Balloon GetChangedXML()
   3:  {
   4:      CspParameters cspParams = new CspParameters();
   5:      cspParams.KeyContainerName = "GELIS_RSA_KEY";
   6:  
   7:      // 建立一個新的 RSA key 並儲存至 RSACryptoServiceProvider 的容器中. 
   8:      RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
   9:  
  10:      Balloon balloon = new Balloon()
  11:      {
  12:          MyBalloonChilder = new BalloonChilder()
  13:          {
  14:              CarName = "Toyota Corolla Altis 2014"
  15:          }
  16:      };
  17:  
  18:      XmlDocument xmlDoc = new XmlDocument();
  19:  
  20:      xmlDoc.PreserveWhitespace = true;
  21:  
  22:      string XmlString = XmlHelper.SerializeAnObject(balloon);
  23:  
  24:      xmlDoc.LoadXml(XmlString);
  25:      XmlHelper.SignXml(xmlDoc, rsaKey);
  26:  
  27:      Balloon result = XmlHelper.DeSerializeAnObject<Balloon>(xmlDoc.InnerXml);
  28:  
  29:      return result;
  30:  }

 

如上程式碼,你可以在 IE 的回傳中看見 XML Signature 的區段如下:

image

如上,基本的 XML Signature 應用。下次筆者介紹如何驗證 XML 是否有被竄改。

 

註:

剛忘了說明,如果要在序列化輸出的 XML 文件中有 <Signature> 的區段,得在 Balloon 類別中宣告 Signature 屬性,並指定命名空間為http://www.w3.org/2000/09/xmldsig#,以及自行宣告 Signature 類別所需相屬性及類別。

image


 

簽名:

學習是一趟奇妙的旅程

這當中,有辛苦、有心酸、也有成果。有時也會有瓶頸。要能夠繼續勇往直前就必須保有一顆最熱誠的心。

軟體開發之路(FB 社團)https://www.facebook.com/groups/361804473860062/

Gelis 程式設計訓練營(粉絲團)https://www.facebook.com/gelis.dev.learning/


 

如果文章對您有用,幫我點一下讚,或是點一下『我要推薦,這會讓我更有動力的為各位讀者撰寫下一篇文章。

非常謝謝各位的支持與愛護,小弟在此位各位說聲謝謝!!! ^_^