熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Javascript >> 正文

Java SE 6中XML數字簽名標准Java接口

2013-11-23 17:56:11  來源: Javascript 

  級別 中級

  孫 瑛霖 軟件工程師 IBM 中國軟件開發實驗室 SOA設計中心

  

    XML 數字簽名技術用於對 XML 格式的數據進行數字簽名以保證報文的完整性不可否認性以及提供身份認證信息JSR 提供了 XML 數字簽名的 Java 接口而最近發布的 Java SE 則包括了 JSR 的 實現從而為基於 Java 的應用程序提供了標准的 XML 數字簽名接口本文首先簡要介紹技術背景以實例來講解 XML 數字簽名的語法和處理規則之後用具體的程序例子解釋如何使用 Java SE 生成各種格式的 XML 數字簽名並進行驗證

   技術背景

   數字簽名簡介

  數字簽名是非對稱密鑰技術的一種應用模式用於保證報文的完整性不可否認性以及提供身份認證信息數字簽名的原理如圖 所示


數字簽名的原理
數字簽名的原理

  發送者在發送報文之前先選用某種摘要算法為報文生成一個摘要值並使用自己的私鑰對摘要值加密然後將加密後的摘要附在報文後面一同發送給報文的接收者接收者收到報文後從中分離出原始報文和加密後的報文摘要使用與發送者相同的摘要算法計算原始報文的摘要值 D並使用發送者的公共密鑰將加密後的報文摘要解密得到摘要值 D檢查 DD 是否匹配

  如果匹配那麼由於密鑰對的唯一性所以可以確定報文發送者的身份而且由於數據摘要算法的特點還可以確定原始報文在傳輸過程中沒有被篡改

   XML 數字簽名簡介

  XML 發展至今已經逐漸成為標准的數據描述技術在分布式應用中廣泛地用於數據的交換由於 XML 數據本身的特殊性和使用 XML 進行數據傳輸的分布式應用的特點在對 XML 文檔的特定部分進行簽名多方簽名以及簽名後保持 XML 文檔原有的良構特性等諸多方面傳統的數字簽名技術都無法很好地實現

  基於這樣的問題WC 組織制訂了 XML 數字簽名規范規定了標准的 XML 數字簽名語法和處理規則同傳統意義的數字簽名相比XML 數字簽名能夠對 XML 文檔進行細粒度地分析支持多種方式的文檔數據轉換只對文檔的特定部分進行簽名和驗證並且能夠保持 XML 文檔的良構特性此外XML 數字簽名提供的密鑰信息表示方法清晰易讀更加便於簽名的自動驗證處理

   XML 數字簽名實例

  本節用一個簡單的例子來介紹 XML 數字簽名的語法和處理規則


簽名前的 XML 文檔

   <?xml version=?> <PaymentInfo xmlns=> <CustomerName>Peter</CustomerName> <Amount></Amount> <CreditCardInfo> <ID> </ID> <Issuer>CMB</Issuer> <Expiration>/</Expiration> <Currency>USD</Currency> </CreditCardInfo> </PaymentInfo>


  表 中的 XML 文檔描述了 Peter 的信用卡支付記錄在按照 XML 數字簽名規范對整個文檔簽名之後生成的 XML 文檔如表 所示


簽名後的 XML 文檔

   <?xml version= encoding=UTF standalone=no?> <PaymentInfo xmlns=> <CustomerName>Peter</CustomerName> <Amount></Amount> <CreditCardInfo> <ID> </ID> <Issuer>CMB</Issuer> <Expiration>/</Expiration> <Currency>USD</Currency> </CreditCardInfo> <Signature xmlns=#> <SignedInfo> <CanonicalizationMethod Algorithm=xmlcn#WithComments/> <SignatureMethod Algorithm=#dsasha/> <Reference URI=> <Transforms> <Transform Algorithm=#envelopedsignature/> </Transforms> <DigestMethod Algorithm=#sha/> <DigestValue>gETxLImhuTZtMkmGlybtZWag=</DigestValue> </Reference> </SignedInfo> <SignatureValue> OIFpnZmeGt+tOywzTgrcYBje/uGmGIrbZYYxxXjjsBdq+JwZA== </SignatureValue> <KeyInfo> <KeyValue> <DSAKeyValue> <P> /KaCzoSyromzEQSbbBsFeyetKIIWF BuRpHtjQTxeEuImbzRMqzVDZkVGxDnNkuFw== </P> <Q>lidzDacuoJgmtqEmTRuOMU=</Q> <G> ZRxsnqcEpGknFFHxqaryRPBaQkhpMdLRQnG Awtx/XPaFBpsypNWMOHCBiNUNogpsQWQvnlMpA== </G> <Y> NMxkCcOHddqBJFQGPpzIncSonBPshqlxsd pAqgMlgnkCPHqfOjLxNPZwHeDtHLfKKSYg+LnXzw== </Y> </DSAKeyValue> </KeyValue> </KeyInfo> </Signature> </PaymentInfo>


  所有與 XML 數字簽名相關的信息都存放在 <Signature> 元素中<Signature> 元素包含有幾個主要的子元素


<Reference> 元素至少包含一個 <Reference> 元素每個 <Reference> 元素用於對待簽名數據進行引用包含有引用方式轉換方法摘要算法和摘要值等信息<Reference> 還包含有 XML 數據的規則化方法並指定了數字簽名所使用的算法
<SignatureValue> 元素包含對 <Reference> 元素規范化後的內容進行簽名生成的數字簽名的值
<KeyInfo> 元素用於指定驗證簽名所需的公共密鑰相關信息

  XML 數字簽名的過程大致為


根據每個 <Reference> 元素中指定的資源引用方式摘要算法數據轉換方法等信息對引用資源進行轉換然後對轉換後的結果計算出摘要值
根據 <SignedInfo> 元素中指定的 XML 數據的規范化方法對 <SignedInfo> 規則化對規范化之後的數據生成摘要值並使用私鑰對摘要值進行加密將生成的加密摘要值存放在 <SignatureValue> 元素中

   XML 數字簽名的驗證

  XML 數字簽名的驗證主要包括兩個步驟首先需要對 <SignedInfo> 元素中包含的數據引用部分進行驗證然後對整個 <SignedInfo> 元素的簽名值進行驗證其間任何一步驗證失敗則代表整個 XML 數字簽名驗證失敗

   對數據引用的驗證

  對 <SignedInfo> 中每一個 <Reference> 執行如下驗證步驟


) 應用指定的數據轉換方法取得引用的數據對象
) 使用指定的摘要生成算法生成摘要值
) 將生成的摘要值同 <Reference><DigestValue> 元素包含的摘要值相比較如果不匹配則驗證失敗

   對 <SignedInfo> 簽名值的驗證


) 從 <KeyInfo> 元素中的 <KeyValue> 元素或者根據 <KeyInfo> 元素中指定的信息從外部獲取用於驗證數字簽名的數據發送方公共密鑰
) 使用驗證密鑰將 <SignatureValue> 元素中的加密簽名值解密得到值 D
) 使用 <SignatureMethod> 元素指定的簽名算法對規則化之後的 <SignedInfo> 元素計算摘要值得到值 D
) 判斷 DD 是否匹配如果不匹配則驗證失敗

   XML 數字簽名的Java實現

  在 WC 推出 XML 數字簽名規范之後不久很多組織和廠商就已經開始提供實現產品目前除了各大廠商推出的實現產品之外應用比較廣泛的開源產品是 Apache XML Security 項目該項目實現了 WC 的 XML 數字簽名規范和 XML 加密規范並且提供 Java 和 C++ 兩個版本供用戶選用

  JSR (Java XML Digital Signature API Specification) 規定了 XML 數字簽名規范的標准 Java 實現接口 日最終發布隨後 年秋季發布的 Java SE (產品代號 Mustang) 將 JSR 納入 Java 標准庫中為基於 Java 的上層應用提供標准的 XML 數字簽名支持從此需要使用 XML 安全特性的 Java 項目有了來自 Java 核心平台的基礎支持再也不需要為選擇合適的第三方產品而煩惱

   使用 Java SE 生成 XML 數字簽名並驗證

  本節使用具體的程序例子介紹如何使用 Java SE 中的標准 Java 接口生成各種格式的 XML 數字簽名並進行驗證所有的程序例子都使用下表中的 XML 文檔其中主要包含有 Simon 和 Peter 二人的信用卡支付記錄

   程序中使用的 XML 文檔

   <?xml version=?> <SalesData> <PaymentInfo xmlns=> <CustomerName>Simon</CustomerName> <Amount></Amount> <CreditCardInfo> <ID> </ID> <Issuer>CMB</Issuer> <Expiration>/</Expiration> <Currency>USD</Currency> </CreditCardInfo> </PaymentInfo> <PaymentInfo xmlns= id=PeterPayment> <CustomerName>Peter</CustomerName> <Amount></Amount> <CreditCardInfo> <ID> </ID> <Issuer>CMB</Issuer> <Expiration>/</Expiration> <Currency>USD</Currency> </CreditCardInfo> </PaymentInfo> </SalesData>


  本節的程序基於 Java SE 請讀者自行下載安裝並配置開發環境

   生成並驗證 Enveloped 格式的 XML 數字簽名

  Enveloped 格式的簽名指簽名元素包含於被簽名數據中如表 所示

   Enveloped格式的 XML 數字簽名

   <SignedData> <Signature xmlns=#> </Signature> </SignedData>


   生成簽名

   創建 XMLSignatureFactory 實例

  XMLSignatureFactory 是與簽名相關的 XML 元素對象的創建工廠本文在這裡創建以DOM 處理機制實現的 XMLSignatureFactory 實例

   創建對整個 XML 文檔的引用

  這一步創建 <Reference> 元素引用整個 XML 文檔

  

   Transform envelopedTransform = facnewTransform(TransformENVELOPED TransformParameterSpec)null); DigestMethod shaDigMethod = facnewDigestMethod(DigestMethodSHA null); Reference refToRootDoc = facnewReference( shaDigMethod CollectionssingletonList(envelopedTransform) null null);


  創建 Reference 的時候將 URI 參數指定為 表示對整個 XML 文檔進行引用摘要算法指定為 SHA這裡將轉換方式指定為 ENVELOPED 這樣在對整個文檔進行引用並生成摘要值的時候<Signature> 元素不會被計算在內

   創建 <SignedInfo> 元素

  <Reference> 元素創建好之後下一步是創建 <SignedInfo> 元素

  

   CanonicalizationMethod cnWithCommentMethod = facnewCanonicalizationMethod( CanonicalizationMethodINCLUSIVE_WITH_COMMENTS (CNMethodParameterSpec) null); SignatureMethod dsa_shaSigMethod = facnewSignatureMethod(SignatureMethodDSA_SHA null); SignedInfo signedInfo = facnewSignedInfo(cnWithCommentMethod dsa_shaSigMethod CollectionssingletonList(refToRootDoc));


  因為最終的數字簽名是針對 <SignedInfo> 元素而生成的所以需要指定該 XML 元素的規范化方法以確定最終被處理的數據這裡指定為 INCLUSIVE_WITH_COMMENTS 表示在規范化 XML 內容的時候會將 XML 注釋也包含在內

  至此待簽名的內容(<SignedInfo> 元素)已指定好再只需要簽名所使用的密鑰就可以創建數字簽名了

   創建密鑰對

  XML 數字簽名規范規定了多種在 <KeyInfo> 中指定驗證密鑰的方式比如 <KeyName><KeyValue><XData><PGPData> 等等這裡使用 XML 數字簽名規范規定必須實現的 <DSAKeyValue> 來指定驗證簽名所需的公共密鑰在程序中使用 javasecurity 包生成 DSA 密鑰對

  首先創建密鑰對

  

   KeyPairGenerator kpGen = KeyPairGeneratorgetInstance(DSA); kpGeninitialize(); KeyPair keyPair = kpGengenerateKeyPair();


  然後以公鑰為參數創建 <KeyValue> 元素

  

   KeyInfoFactory keyInfoFac = facgetKeyInfoFactory(); KeyValue keyValue = keyInfoFacnewKeyValue(keyPairgetPublic());


  根據創建好的 <KeyValue> 元素創建 <KeyInfo> 元素

  

   KeyInfo keyInfo = keyInfoFacnewKeyInfo(CollectionssingletonList(keyValue));


  這裡創建的密鑰對其中的公鑰已經用於創建 <KeyInfo> 元素並存放在其中供簽名驗證使用而其中的私鑰則會在下一步被用於生成簽名

   創建 <Signature> 元素

  前面已經創建好 <SignedInfo><KeyInfo> 元素為了生成最終的數字簽名需要根據這兩個元素先創建 <Signature> 元素然後進行簽名創建出 <SignatureValue> 元素

  

   XMLSignature signature = facnewXMLSignature(signedInfo keyInfo);


     XMLSignature 類中的 sign 方法用於對文檔進行簽名在調用 sign 方法之前還需要創建 DOMSignContext 對象為方法調用提供上下文信息包括簽名所使用的私鑰和最後生成的 <Signature> 元素所在的目標父元素

  

   DocumentBuilderFactory dbf = DocumentBuilderFactorynewInstance(); dbfsetNamespaceAware(true); Document doc = dbfnewDocumentBuilder()parse(new FileInputStream(args[])); DOMSignContext dsc = new DOMSignContext(keyPairgetPrivate() docgetDocumentElement());


  這裡首先使用 JAXP 的 DOM 接口將待簽名文檔解析然後根據前面創建的私鑰和待簽名文檔的根元素創建 DOMSignContext 對象

  請注意到這裡為止都只是創建各種生成簽名所需數據的 XML 元素表示並沒有開始真正地生成數字簽名

   最後一步生成簽名

  

   signaturesign(domSignCtx);


      sign 方法會生成簽名值並作為元素值創建 <SignatureValue> 元素然後將整個 <Signature> 元素加入為待簽名文檔根元素的直接子元素

   輸出簽名後的文檔

  數字簽名生成之後使用 JAX P的 XML 轉換接口將簽名後的 XML 文檔輸出查看簽名結果

  

   TransformerFactory tf = TransformerFactorynewInstance(); Transformer transformer = tfnewTransformer(); transformertransform(new DOMSource(doc) new StreamResult(Systemout));


   驗證簽名

  本節介紹如何使用 Java SE 提供的 XML 數字簽名 API 對上一節生成的數字簽名進行驗證驗證代碼如表

   驗證 XML 數字簽名

   private static void validate(String signedFile) throws Exception { // Parse the signed XML document to unmarshal <Signature> object DocumentBuilderFactory dbf = DocumentBuilderFactorynewInstance(); dbfsetNamespaceAware(true); Document doc = dbfnewDocumentBuilder()parse(new FileInputStream(signedFile)); // Search the Signature element NodeList nl = docgetElementsByTagNameNS(XMLSignatureXMLNS Signature); if (nlgetLength() == ) { throw new Exception(Cannot find Signature element); } Node signatureNode = em(); XMLSignatureFactory fac = XMLSignatureFactorygetInstance(DOM); XMLSignature signature = facunmarshalXMLSignature(new DOMStructure(signatureNode)); // Get the public key for signature validation KeyValue keyValue = (KeyValue)signaturegetKeyInfo()getContent()get(); PublicKey pubKey = keyValuegetPublicKey(); // Create ValidateContext DOMValidateContext valCtx = new DOMValidateContext(pubKey signatureNode); // Validate the XMLSignature boolean coreValidity = signaturevalidate(valCtx); // Check core validation status if (coreValidity == false) { Systemerrprintln(Core validation failed); // Check the signature validation status boolean sv = signaturegetSignatureValue()validate(valCtx); Systemoutprintln(Signature validation status: + sv); // check the validation status of each Reference List refs = signaturegetSignedInfo()getReferences(); for(int i=; i<refssize(); i++) { Reference ref = (Reference)refsget(i); boolean refValid = refvalidate(valCtx); Systemoutprintln(Reference[+i+] validity status: + refValid); } } else { Systemoutprintln(Signature passed core validation); } }


   解析簽名後生成的 XML 文檔(行 - 行

  對於 Enveloped 格式的 XML 簽名而言生成的 <Signature> 元素位於被簽名的 XML 中所以這裡首先使用 JAXP 將簽名後生成的 XML 文檔解析

   查找簽名元素(行 - 行

  所有與 XML 數字簽名相關的信息都存放在 <Signature> 元素中所以需要先取到 <Signature> 元素由於前面在生成簽名的時候將 <Signature> 元素存放為文檔根元素的直接子元素所以這裡根據元素名 Signature 進行搜索搜索到的第一個元素即為 <Signature> 元素

   構造 <Signature> 元素(行 - 行

  使用 XMLSignatureFactory 類的 unmarshalXMLSignature 方法從 DOM 節點構造出 XMLSignature 對象為下一步驗證簽名作准備

   獲取驗證簽名所需的公共密鑰(行 - 行

  在本例中驗證密鑰以 <DSAKeyValue> 的格式存放於 <KeyInfo> 元素中這裡使用 XMLSignature 對象的接口取出公鑰

   創建DOMValidateContext(行 - 行

  在驗證簽名的過程中需要創建 DOMValidateContext 對象來指定上下文信息參數為前面獲取到的驗證公鑰和 <Signature> 元素

   驗證簽名(行 - 行

  驗證簽名所需的所有信息都已就緒開始使用XMLSignature 對象提供的接口進行驗證

   檢查驗證結果(行 - 行

  如果簽名驗證失敗則分別對 <Reference> 元素的簽名值和其中的每一個引用進行驗證進一步確定導致驗證失敗的原因如果簽名值驗證成功而某個引用驗證失敗則說明是該引用新生成的摘要值與原文檔中的摘要值不匹配導致驗證失敗

  以簽名後生成的 XML 文檔作為輸入執行驗證程序從程序的輸出信息可以判斷出驗證成功

  

  Signature passed core validation


  接下來將包含有 <Signature> 元素的待驗證文檔作一點改動把 Peter 的支付信息中的金額改為 美元再次執行程序進行驗證則驗證失敗

  

  Core validation failed Signature validation status: true Reference[] validity status: false


  輸出信息提示簽名值驗證成功而對整個文檔的引用驗證失敗簽名值驗證成功是因為 <SignedInfo> 沒有改動對文檔的引用驗證失敗是因為前面修改了文檔中的數據

   生成並驗證Enveloping格式的簽名

  Enveloping 格式的簽名指 <Signature> 元素包含著被簽名的數據內容如表 所示

   Enveloping 格式的數字簽名

   <Signature xmlns=#> <SignedData> </SignedData> </Signature>


  在 Enveloping 格式的數字簽名中待簽名的 XML 內容需要通過 URI 或者 Transform 進行引用

   生成簽名

  

   // Create XMLObject refering to Simons payment info DocumentBuilderFactory dbf = DocumentBuilderFactorynewInstance(); dbfsetNamespaceAware(true); Document origDoc = dbfnewDocumentBuilder()parse(new FileInputStream(inputFile)); Element docEle = origDocgetDocumentElement(); Node simonPayment = docElegetElementsByTagName(PaymentInfoem(); XMLStructure content = new DOMStructure(simonPayment); XMLObject xmlObj = facnewXMLObject(CollectionssingletonList(content) SimonPayment null null); // Create the reference to element to be signed Reference ref = facnewReference(#SimonPayment facnewDigestMethod(DigestMethodSHA null)); XMLSignature signature = facnewXMLSignature(si ki CollectionssingletonList(xmlObj) null null);


  為了引用待簽名的內容首先查找到 Simon 的支付記錄對應的 DOM 元素使用 XMLStructure 對其進行包裝然後生成 XMLObject 並為其指定 idSimonPayment隨後在創建 Referenc 的時候同樣指定引用 idSimonPayment在創建 XMLSignature 對象的時候將待簽名的 XMLObject 作為參數這些 XMLObject 包含的 XML 內容將會成為 <Signature> 元素的子元素從而創建出 Enveloping 格式的簽名程序的其他部分與生成 Enveloped 格式的數字簽名相同

  簽名之後生成的 <Signature> 元素如下

  

   <Signature xmlns=#> <SignedInfo> <CanonicalizationMethod Algorithm=xmlcn#WithComments/> <SignatureMethod Algorithm=#dsasha/> <Reference URI=#SimonPayment> <DigestMethod Algorithm=#sha/> <DigestValue> XGesfrCnrKuXCBmwlbofheksg=</DigestValue> </Reference> </SignedInfo> <SignatureValue> kxOvEObeKrcLh+OyJzGKjFBKKLGREtMGgioCZzYpaNH/nyfw== </SignatureValue> <KeyInfo> <KeyValue> <DSAKeyValue> <P> /KaCzoSyromzEQSbbBsFeyetKIIWFBuRpHt jQTxeEuImbzRMqzVDZkVGxDnNkuFw== </P> <Q>lidzDacuoJgmtqEmTRuOMU=</Q> <G> ZRxsnqcEpGknFFHxqaryRPBaQkhpMdLRQnGAwtx /XPaFBpsypNWMOHCBiNUNogpsQWQvnlMpA== </G> <Y> MOqJjUihtTOSwiVnoZSCzmDewfBsyeRLIzgZeiJF jGJHCHvUtzdlzHuubZpRyXIEAxvprKw== </Y> </DSAKeyValue> </KeyValue> </KeyInfo> <Object Id=SimonPayment> <PaymentInfo xmlns=> <CustomerName>Simon</CustomerName> <Amount></Amount> <CreditCardInfo> <ID> </ID> <Issuer>CMB</Issuer> <Expiration>/</Expiration> <Currency>USD</Currency> </CreditCardInfo> </PaymentInfo> </Object> </Signature>


  表 中的驗證程序同樣可以用來驗證上面生成的 Enveloping 格式的 XML 簽名

   生成並驗證Detached格式的簽名

  Detached 格式的簽名指 <Signature> 元素與被簽名的數據內容之間是彼此分離的既不是包含關系也不是被包含關系如下表所示

  

   <SignedData> </SignedData> <Signature xmlns=#> </Signature>


  Detached 格式多用於對外部獨立的數據對象進行簽名使用 URI 來引用外部數據對象也可以引用同一 XML 文檔中的其他元素對之進行數字簽名

  表中待簽名的 XML 文檔包含有兩次信用卡交易的支付信息第一個是 Simon 的信用卡交易記錄第二個是 Peter 的本節將對 Peter的支付記錄進行數字簽名所使用的簽名格式是 Detached 格式為了讓 <Signature> 元素能夠引用到包含有 Peter 信用卡交易信息的 XML 元素這裡用 id 屬性對 Peter 的 <PaymentInfo> 元素加以引用

   生成簽名

  本節的大部分代碼都與生成 Enveloped 格式簽名的代碼相同只是在創建 Reference 的時候有些不同

  

   XMLSignatureFactory fac = XMLSignatureFactorygetInstance(DOM); // Create the reference to Peters payment info DigestMethod shaDigMethod = facnewDigestMethod(DigestMethodSHA null); Reference ref = facnewReference(#PeterPayment shaDigMethod);


  #PeterPayment 用於引用位於同一文檔中的 Peter 的 <PaymentInfo> 元素

   驗證簽名

  使用表 中的 validate 方法對生成的數字簽名進行驗證輸出信息表示驗證成功然後在生成的 XML 文檔中將 Simon 的支付金額改為 重新驗證結果依然為驗證成功這是因為只對 Peter 的支付記錄進行簽名所以 Simon 的支付信息改變不會影響數字簽名的驗證結果如果再將 Peter 的支付金額改為 重新驗證則輸出信息顯示驗證失敗

   總結

  同傳統意義的數字簽名技術相比XML 數字簽名技術有很多不可替代的優點它能夠在保持 XML 文檔良構性的前提下對文檔內容進行細粒度的簽名和驗證通過資源引用和轉換的機制擴大了簽名的作用范圍更能夠滿足分布式應用系統中的安全需求

  WC 組織制訂的 XML 數字簽名規范規定了標准的 XML 簽名語法和處理規則而Java SE 則為 XML 數字簽名提供了標准的 Java 接口目前這些 XML 數字簽名規范和程序標准還在進一步完善中相信隨著技術的發展XML 數字簽名技術必將得到越來越廣泛的應用

  參考資料


Donald E Eastlake Joseph M Reagle David Solo XMLSignature Syntax and Processing (core/) WC Recommendation Feb


JSR - XML Digital Signature APIs()


JSR - XML Digital Encryption APIs()


Apache XML Security()


Sun Java SE ()


developerWorks 中國網站 XML 技術專區


developerWorks 中國網站 Java 技術專區

  關於作者

  

  孫瑛霖軟件工程師現就職於IBM中國SOA設計中心對網絡與分布式系統安全和SOA等技術有著濃厚的興趣


From:http://tw.wingwit.com/Article/program/Java/Javascript/201311/25379.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.